<html>
<body>
<script type="application/javascript;version=1.7" src="kbtrt.js"></script>
<script type="application/javascript;version=1.7">
function Test17 () { let a = 1; yield "yes, this browser supports js 1.7"; }
var appCanvas,appGC;
document.write ("<canvas width=480 height=640 id=appCanvas></canvas>"+
"<div id=appOffscreenPool style='visibility: hidden; position: absolute; left: 0; top: 0'></div>"+
"<div id=appLog></div>");
var
	appCanvas = document.getElementById ('appCanvas'),
	appGC = appCanvas.getContext ('2d'),
	appLog = document.getElementById ('appLog'),
	appOffscreenPool = document.getElementById ('appOffscreenPool');

appLog.writef = function (fmt,data)
{
	if (data==undefined)
		appLog.innerHTML += fmt;
	else
		appLog.innerHTML += Kbt.format (fmt,data);
};

//var cp1 = new Date ().getTime ();

//
// start of precompiled code
//

//
// coreutil.kbtjsx
// Library of common use helpers
//

//
// helpers
//

/// Dispose every object in array arr which implements MxDisposable, then clear
/// the array
/// @tparam Array arr Array containing disposabless
function disposeArray (arr)
{
	var i = arr.length;
	while (--i>=0)
	{
		let asDisposable = MxDisposable.dynamicCast (arr);
		if (asDisposable) asDisposable.dispose ();
	}
	arr.length = 0;
}

//
// Point
//

/// Incapsulates a point (x,y)
function Point (x,y)
{
	this.x = x;
	this.y = y;
}

/// Return point which is this point plus p
/// @tparam Point p
/// @treturn Point this+p
Point.prototype.add = function (p)
{
	return new Point (this.x+p.x,this.y+p.y);
};

/// Return point which is this point minus p
/// @tparam Point p
/// @treturn Point this-p
Point.prototype.sub = function (p)
{
	return new Point (this.x-p.x,this.y-p.y);
};

/// Return point which is this point multiplied by scalar a
/// @tparam Number a
/// @treturn Point a*this
Point.prototype.mul = function (a)
{
	return new Point (this.x*a,this.y*a);
};

/// Return negated point (same as mul (-1) but a bit faster)
/// @treturn Point -this
Point.prototype.neg = function ()
{
	return new Point (-this.x,-this.y);
};

/// Return true if this point is equal to p or false otherwise
/// @tparam Point p Point to compare to
/// @treturn Boolean true if this point is equal to p or false otherwise
Point.prototype.equal = function (p)
{
	return this.x==p.x && this.y==p.y;
};

/// Return length of the point as of vector
/// @treturn Number Length of the point as of vector starting in 0,0 and ending
/// in the point
Point.prototype.length = function ()
{
	return Math.sqrt (this.x*this.x+this.y*this.y);
};

/// Return the point normalized as the vector. You must provide an epsilon guard
/// value (it should be close to 0 but not 0), if the vector length is less than
/// epsilon then the value returned is zero vector
/// @tparam Number eps Guard length minimum threshold, all shorter vectors are
/// considered zero vectors
/// @treturn Point The normalized vector (or zero vector if this is shorter than
/// eps)
Point.prototype.normalize = function (eps)
{
	var len = this.length ();
	return len>=eps? this.mul (1.0/len) : new Point (0,0);
};

//
// Size
//

/// Incapsulates size (width,height)
function Size (w,h)
{
	this.width = w;
	this.height = h;
}

/// Return true if this size is equal to s or false otherwise
/// @tparam Size s Size to compare to
/// @treturn Boolean true if this size is equal to s or false otherwise
Size.prototype.equal = function (s)
{
	return this.width==s.width && this.height==s.height;
};

/// Return size to which dimensions are added those of s
/// @tparam Size s Size to add
/// @treturn Size Size with dimensions equal to those of this plus those of s
Size.prototype.add = function (s)
{
	return new Size (this.width+s.width,this.height+s.height);
};

/// Return size to which dimensions are subtracted those of s
/// @tparam Size s Size to subtract
/// @treturn Size Size with dimensions equal to those of this minus those of s
Size.prototype.sub = function (s)
{
	return new Size (this.width-s.width,this.height-s.height);
};

/// Return this size with its dimensions negated
/// @treturn Size This size with negated dimensions
Size.prototype.neg = function ()
{
	return new Size (-this.width,-this.height);
};

/// If parameter is number, returns this size with dimensions multiplied by
/// the number. If parameter is size, returns this size with each dimension
/// multiplied by corresponding dimension of parameter (use to calculate size
/// of a rectangular grid by multiplying size of cell by size of grid in cells).
/// @tparam Number|Size sOrNumber Number or size to multiply this by
/// @treturn Size The result
Size.prototype.mul = function (sOrNumber)
{
	return sOrNumber instanceof Size
		? new Size (this.width*sOrNumber.width,this.height*sOrNumber.height)
		: new Size (this.width*sOrNumber,this.height*sOrNumber);
};

//
// Rectangle
// (terms: rectangle is specified by reference point, width and height,
// 'normalized' rectangle means rectangle with positive width and height, where
// the reference point takes place of top left corner; many operations either
// require rectangle to be normalized or use its normalized version implicitly)
//

/// Incapsulates rectangle (x,y of top left, width,height)
function Rect (x,y,w,h)
{
	this.x = x;
	this.y = y;
	this.width = w;
	this.height = h;
}

/// Get rectangle by two corner points
/// @tparam p1 Point 1, assumed top left corner
/// @tparam p2 Point 2, assumed bottom right corner
/// @treturn Corresponding rectangle
Rect.fromTwoPoints = function (p1,p2)
{
	return new Rect (p1.x,p1.y,p2.x-p1.x,p2.y-p1.y);
};

/// Get rectangle by given point and size
Rect.fromPointAndSize = function (p,s)
{
	return new Rect (p.x,p.y,s.width,s.height);
};

/// Empty rectangle
Rect.empty = new Rect (undefined,undefined,undefined,undefined);

/// Return true if the rectangle is empty, false if not
/// @treturn Boolean true if rectangle is empty or false if it isn't
Rect.prototype.isEmpty = function ()
{
	return this.x===undefined;
};

/// Return true if the rectangle is equal to r, false if not. Ok to compare
/// to an empty rectangle.
/// @treturn Boolean true if this rectangle is equal to r or false if it isn't
Rect.prototype.equal = function (r)
{
	return this.x==r.x &&
		this.y==r.y &&
		this.width==r.width &&
		this.height==r.height;
};

/// Calculate and return intersection of this and r. Normalized versions of both
/// rects are used, and result is normalized too.
/// @tparam Rect r Rectangle to calculate intersection with
/// @treturn Rect Intersection of this and r
Rect.prototype.intersection = function (r)
{
	if (this.isEmpty () || r.isEmpty ()) return Rect.empty;
	var r1 = this.normalize (),r2 = r.normalize ();
	if (Math.max (r1.x,r1.x+r1.width,r2.x,r2.x+r2.width)-
		Math.min (r1.x,r1.x+r1.width,r2.x,r2.x+r2.width)>=
		r1.width+r2.width ||
		Math.max (r1.y,r1.y+r1.height,r2.y,r2.y+r2.height)-
		Math.min (r1.y,r1.y+r1.height,r2.y,r2.y+r2.height)>=
		r1.height+r2.height)
		return Rect.empty; // rects do not cross
	var x1 = Math.max (r1.x,r2.x),
		y1 = Math.max (r1.y,r2.y),
		x2 = Math.min (r1.x+r1.width,r2.x+r2.width),
		y2 = Math.min (r1.y+r1.height,r2.y+r2.height);
	return new Rect (x1,y1,x2-x1,y2-y1);
};

/// Calculate and return rectangle big enough to include both this and Point p
/// p can be Rect, then the result will be rectangle big enough to include both
/// this and the p rectangle (which can also be empty).
/// @tparam Point|Rect p Point or rect to calc union with
/// @treturn Rect Resulting rectangle, normalized
Rect.prototype.union = function (p)
{
	if (p instanceof Rect)
	{
		if (p.isEmpty ()) return this.normalize ();
		else return this.union (p.leftTop ()).union (p.RightBottom ());
	}
	else
		if (this.isEmpty ())
			// rect is empty
			return new Rect (p.x,p.y,0,0);
		else
		{
			var rn = this.normalize (),
				x1 = rn.x,y1 = rn.y,x2 = rn.x+rn.width,y2 = rn.y+rn.height;
			if (p.x<x1) x1 = p.x;
			if (p.x>x2) x2 = p.x;
			if (p.y<y1) y1 = p.y;
			if (p.y>y2) y2 = p.y;
			return new Rect (x1,y1,x2-x1,y2-y1);
		}
};

/// Return normalized rectangle, i. e. the same rectangle with guaranteed
/// non-negative dimensions
/// @treturn Rect Rectangle geometrically same as this but with non-negative
/// width and height
Rect.prototype.normalize = function ()
{
	return this.isEmpty ()? Rect.empty :
		new Rect (this.width>0? this.x : this.x+this.width,
			this.height>0? this.y : this.y+this.height,
			Math.abs (this.width),Math.abs (this.height));
};

/// Return rectancle's area. 0 for empty rect.
/// @treturn Rectangle's area, 0 for empty rect
Rect.prototype.area = function ()
{
	return this.isEmpty ()? 0 : this.width*this.height;
};

/// Shift rectangle by vector equal to point p
/// @treturn Rect Rectangle shifted by point p
Rect.prototype.add = function (p)
{
	return new Rect (this.x+p.x,this.y+p.y,this.width,this.height);
};

/// Shift rectangle by vector equal to point -p
/// @treturn Rect Rectangle shifted by point -p
Rect.prototype.sub = function (p)
{
	return new Rect (this.x-p.x,this.y-p.y,this.width,this.height);
};

/// Return rectangle's center
/// @treturn Point Center point of the rectangle
Rect.prototype.center = function ()
{
	return new Point (this.x+this.width*0.5,this.y+this.height*0.5);
};

/// Return rectangle's left top corner. Assumed the rectangle is non-empty and
/// normalized.
/// @treturn Point Top-left point of the rectangle, if the rectangle is
/// normalized, or the reference point (this.x, this.y) otherwise
Rect.prototype.leftTop = function ()
{
	return new Point (this.x,this.y);
};

/// Return rectangle's bottom right corner. Assumed the rectangle is non-empty
/// and normalized.
/// @treturn Point Bottom-right point of the rectangle, if the rectangle is
/// normalized, or the point opposite to reference one otherwise
Rect.prototype.rightBottom = function ()
{
	return new Point (this.x+this.width,this.y+this.height);
};

/// Return rectangle's relative point. Assuming the rectangle is non-empty and
/// normalized. Argument is point, (0,0) corresponds to leftTop, (0.5,0.5) to
/// center, (1,1) to rightBottom. (1,0) is top right, (0,1) is bottom left.
/// You can specify coordinates beyond this range to get points outside the
/// rectangle.
/// @tparam Point p Point describing relative coordinates within the rect,
/// with (0,0) and (1,1) corresponding to its opposite corners
/// @treturn Point Corresponding point on the rectangle
Rect.prototype.relativePoint = function (p)
{
	return new Point (this.x+this.width*p.x,this.y+this.height*p.y);
};

Rect.prototype.size = function ()
{
	return new Size (this.width,this.height);
};

/// Return true if point p is inside this rectangle (or on its edge)
/// @tparam Point p Point to check
/// @treturn Boolean true if point is in the rectangle, false otherwise
Rect.prototype.isInside = function (p)
{
	if (this.isEmpty ()) return false;
	var nr = this.normalize ();
	return p.x>=nr.x && p.y>=nr.y && p.x<=nr.x+nr.width && p.y<=nr.y+nr.height;
};

//
// Macro helpers
//

// only use with var names or members of objects that are calculated by exprs
// w/o side effects, since the expression will be calculated from once to thrice


var Layouts =
{
	common:
	{
		//logoKbt: new Rect (10,510,120,120)
	},

	popup:
	{
		width: 360,
		widthMargin: 16, // left/right edge to content
		topBorderHeight: 24,
		topMarginHeight: 24, // top edge to content
		bottomBorderHeight: 24,
		bottomMarginHeight: 48, // section with buttons
		buttonWidth: 128,
		buttonHeight: 24,
		twoButtonMargin: 32, // X-space between 2 buttons
		textWidth: 294, // width of text component
		marginHeight: 8 // height of margin component
	},

	screenLoading:
	{
		progressBar: new Rect (25,210,430,10),
		progressLabel: new Rect (25,185,430,20),
		goButtY: 400
	},

	screenMainMenu:
	{
		buttPlay: new Rect (240,300,240,24),
		buttDontPlay: new Rect (240,352,240,24),
		buttAbout: new Rect (240,404,240,24)
	},

	screenGame:
	{
		boardRect: new Rect (45,55,384,384),
		clipCenterY: 506,
		clipCenterX: 240,
		clipCellWidth: 32,
		clipCellHeight: 48,
		specularOffset: new Point (-8,5),
		selectedPonyScale: new Point (1.2,1.2),
		labelScore: new Rect ( 480 -200,0,200,16),
		labelRecycleRate: new Rect (0, 640 -16,200,16),
		labelAmmoLeft: new Rect ( 480 -200, 640 -16,200,16)
	}
};


//
// coordinator.kbtjsx
// Defines application event queue and its typical users
//

//
// Application event queue. The core coordinator of our everything.
//

var appEQ = new MxEventQueue ();

/// Send an event and process it immediately if possible. Only use from under
/// callbacks invoked directly by browser.
appEQ.send = function (evt)
{
	this.post (evt);
	this.doProcess ();
};

/// Send an event persistently and process it immediately if possible. Only use
/// from under callbacks invoked directly by browser.
appEQ.sendPersistent = function (evt)
{
	this.postPersistent (evt);
	this.doProcess ();
};

appEQ.sendStatic = function (evt)
{
	this.postStatic (evt);
	this.doProcess ();
};

appEQ.sendStaticPersistent = function (evt)
{
	this.postStatic (evt);
	this.doProcess ();
};

/// Perform handling of events in a trap harness. Use from
appEQ.doProcess = function ()
{
	try
	{
		this.processEvents ();
	}
	catch (e)
	{
		if (mainInstance) mainInstance.dispose ();
		appLog.writef ("<hr>Fuckup detected<hr>\n");
		if (e.elements)
		for (var x in e.elements ()) appLog.writef (x+"<br>\n");
		else appLog.writef (e.message);
	}
};

/// Perform a checkpoint and process accumulated events. Only use from under
/// subscribed coroutines via #[appEQ.checkpoint] (). Coroutines must be
/// subscribed to event type "checkpoint".
appEQ.checkpoint = function ()
{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
	var cpin,cpoint = { eventType: "checkpoint" };
	appEQ.postPersistent (cpoint);
	do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[cpin] = [_Kbt$msg];_Kbt$consumed = true; if(cpin==cpoint){;break;}else _Kbt$consumed = false;}} while (false);
}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;};

/// Blocks coroutine forever. May be used from any coroutine. Useful if
/// the actual work is done by its observers.
appEQ.endlessLoop = function ()
{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
	var blockme,bm = {};
	do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[blockme] = [_Kbt$msg];_Kbt$consumed = true; if(blockme===bm){;break;}else _Kbt$consumed = false;}} while (false);
}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;};

/// Perform waiting for a given number of milliseconds. Only use from under
/// subscribed coroutines via #[appEQ.delay] (). Coroutines must be
/// subscribed to event type "timerTick".
appEQ.sleep = function (ms)
{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
	var tick;
	var sd,sde = { eventType: "sleepDone" };
	 do { tick = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(tick)(tick.eventType=="timerTick")
	),false,kbtBind (this,function(tick){
		if ((ms -= appLastFrameElapsed) <= 0)
			appEQ.postPersistent (sde);
	}
	));_Kbt$as.markToDispose(tick);} while (false)do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[sd] = [_Kbt$msg];_Kbt$consumed = true; if(sd===sde){;break;}else _Kbt$consumed = false;}} while (false);
}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;};

/// Expect exactly for a given event object (typically used for MxStaticEvent).
appEQ.expect = function (obj)
{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
	var evtIn;
	do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[evtIn] = [_Kbt$msg];_Kbt$consumed = true; if(evtIn==obj){;break;}else _Kbt$consumed = false;}} while (false);
}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;};

var _$appAsyncDoer = new function ()
{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
	_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func,["asyncAction"])));

	for (;;)
	{
		var asyncAction;
		do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[asyncAction] = [_Kbt$msg];_Kbt$consumed = true; if(asyncAction.eventType=="asyncAction"){;break;}else _Kbt$consumed = false;}} while (false);
		asyncAction.action ();
	}
}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;};

/// Perform an action asynchronously
/// @tparam Function action Action to perform (#delegate recommended)
function doAsync (action)
{
	appEQ.post ({ eventType: "asyncAction", action: action });
}

/// Helper: quick maker of a MxStaticEvent from a given object, by mixing
/// MxStaticEvent into it. Has no effect on object which is already a static
/// event.
/// @tparam A (non-static) event object
/// @treturn Same object with MxStaticEvent implanted into it
function makeStaticEvent (evtObj)
{
	Kbt.Mixin.enable (evtObj);
	evtObj.mxEnable (MxStaticEvent);
	return evtObj;
}

/// Helper: quick maker of a MxAsyncSignal from a given object, by mixing
/// MxAsyncSignal into it. Has no effect on object which is already a
/// MxAsyncSignal. (See description of MxAsyncSignal below.)
/// @tparam A (non-static) event object
/// @treturn Same object with MxStaticEvent implanted into it
function makeAsyncSignal (evtObj)
{
	Kbt.Mixin.enable (evtObj);
	evtObj.mxEnable (MxAsyncSignal);
	return evtObj;
}

//
// Mixin for asyncronous completion signals and barrier
//

/// Asynchronous completion notifier. A static event that is posted
/// persistently upon completion.
var MxAsyncSignal = Kbt.Mixin.declare ([MxStaticEvent],
{
	/// Set this signal to wait for result of async operation op. As the op is
	/// completed, the async signal is posted statically and persistently into
	/// appEQ. If the signal is already set to wait for some other op, it's
	/// resubscribed to op. If the op is already completed, the signal is posted
	/// immediately.
	/// @tparam MxAsyncOp op Operation to wait for, must not be null
	asgWaitFor: Kbt.Func.placeholder, // (MxAsyncOp op)

	/// Cancels waiting set by last asgWaitFor. The signal will no longer track
	/// that event. Has no effect if signal is not waiting for anyone.
	asgUnwait: Kbt.Func.placeholder, // ()

	/// Checks status of the operation waited for. Only returns a meaningful
	/// value if the operation has completed and the signal is posted.
	/// @treturn Boolean True if the operation waited for ended in cancel,
	/// false otherwise
	isCancelled: Kbt.Func.placeholder, // () => bool

	/// Friend to MxAsyncOp. Commands to post self with a given cancel status.
	friend$asgnotify: Kbt.Func.placeholder, // (bool)
},
  function ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var cancelled,
		link = new Kbt.ListNode (this);

	this.asgWaitFor.implement (function (op)
	{
		op = MxAsyncOp.dynamicCast (op) ||
			Kbt.panic ("MxAsyncSignal::asgWaitFor: argument must implement MxAsyncOp");
		this.asgUnwait (); // cancel possibly pending waiting
		op.friend$asgsubscribe (link);
	});

	this.asgUnwait.implement (function (op)
	{
		link.list () && link.list ().remove (link);
		this.withdraw (); // withdraw possibly posted self
	});

	this.friend$asgnotify.implement (function (ok)
	{
		cancelled = !ok;
		this.withdraw ();
		appEQ.sendStaticPersistent (this);
	});

	// destructor agenda
	this.markToDispose(kbtBind(this,function(){ this.asgUnwait (); }
));}catch(e){this.markToDispose(function(){throw e;});this.dispose();}});

/// Mixin that impersonates a pending asynchronous operation. It posts a static
/// event(s) you request persistently upon completion, or you may dispose it to
/// cancel. Idea of use: your function that initiates an async operation creates
/// an object that implements this mixin, where $doCancel is overridden to make
/// actual actions related to cancel. Later, when async operation is completed,
/// the completer should invoke $completed method.
var MxAsyncOp = Kbt.Mixin.declare ([MxDisposable],
{
	/// Return true if the operation is completed or false if it is in progress
	/// @treturn Boolean True is operation is completed, false otherwise
	isCompleted: Kbt.Func.placeholder, // () => bool

	/// Friend to MxAsyncSignal. Add list node of a MxAsyncSignal to
	/// notification list.
	friend$asgsubscribe: Kbt.Func.placeholder, // (Kbt.ListNode sigNodeLink)

	/// Protected. Function to be invoked when the operation is cancelled
	/// (i. e., disposed). Default implementation takes no action. Overridden
	/// implementation should perform the cancel.
	$doCancel: Kbt.Func.virtual (function () {}), // ()

	/// Protected/internal. Function to be invoked at time of actual completion.
	$completed: Kbt.Func.placeholder, // ()
},
  function ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var completed = false,
		cancelled = false,
		subscribers = new Kbt.List ();

	function notifyAndReset ()
	{
		// note: can be invoked from location with different 'this'
		for (var subs in subscribers.elements ())
			subs.friend$asgnotify (!cancelled);
		subscribers.clear ();
	}

	this.isCompleted.implement (function () { return completed; });
	this.friend$asgsubscribe.implement (function (sigNodeLink)
	{
		if (completed) sigNodeLink.value.friend$asgnotify (!cancelled);
		else subscribers.addLast (sigNodeLink);
	});

	this.markToDispose(kbtBind(this,function(){
		if (!completed)
		{
			this.$doCancel ();
			completed = true;
			cancelled = true;
			notifyAndReset ();
		}
	}

	));this.$completed.implement (function ()
	{
		if (!completed)
		{
			completed = true;
			cancelled = false;
			notifyAndReset ();
		}
	});
}catch(e){this.markToDispose(function(){throw e;});this.dispose();}});

/// Class that implements waiting until a given set of async operations is
/// completed (with assumption that neither of them ends in cancel, which is
/// undistinguishable from normal completion for the barrier).
var MxAsyncOpBarrier = Kbt.Mixin.declare ([MxAsyncOp],
{
	/// Begins a set of operations. This function launches the barrier and can
	/// only be called once per lifetime of a barrier object.
	/// @tparam opSet Array of async operations to wait for.
	/// @treturn MxAsyncOp Operation of barrier completion (the barrier itself
	/// actually). This op will be completed when the barrier is triggered,
	/// which will be the case as soon as all operations from opSet are
	/// completed.
	begin: Kbt.Func.placeholder, // (MxAsyncOp[] opSet) => MxAsyncOp

	/// Returns count of scheduled operations that have completed on this
	/// barrier
	/// @treturn Number Numer of completed (or cancelled) operations
	itemsCompleted: Kbt.Func.placeholder, // () => Number

	/// Returns total count of scheduled operations. Progress rate is
	/// itemsCompleted/itemsTotal.
	/// @treturn Number Numer of completed (or cancelled) operations
	itemsTotal: Kbt.Func.placeholder, // () => Number

	/// Returns an event object that is posted (statically, non-persistent)
	/// whenever any of pending operations has completed. eventType is
	/// "MxAsyncBarrier.update".
	/// @treturn MxStaticEvent Event object
	updateSignal: Kbt.Func.placeholder, // () => MxStaticEvent
},
  function ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var
		me = this,
		nItemsCompleted = 0,
		items, // Crt []
		updateSignalObj =
			this.markToDispose((makeStaticEvent ({ eventType: "MxAsyncBarrier.update" })));

	function disposeItems ()
	{
		if (items)
			for (var i in items) items[i].dispose ();
	}
	this.markToDispose(kbtBind(this,function(){ disposeItems (); }

	));function expector (asyncOp)
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func,["barrierOpDone"])));
		// prepare signal
		var doneSignal = _Kbt$as.markToDispose((makeStaticEvent (
			{ eventType: "barrierOpDone" })));
		doneSignal.mxEnable (MxAsyncSignal);
		// init expecting
		doneSignal.asgWaitFor (asyncOp);
		var waitOk = false;
		_Kbt$as.markToDispose(kbtBind(this,function(){
			// to do on exit from the scope
			nItemsCompleted++;
			updateSignalObj.withdraw ();
			appEQ.postStatic (updateSignalObj);
			if (nItemsCompleted>=items.length)
			{
				// all operations completed - post barrier completed event
				me.$completed ();
			}
		}
		));do {let func=new(appEQ.expect )(doneSignal),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
	}

	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}this.begin.implement (function (opSet)
	{
		Kbt.assert (items===undefined,
			"MxAsyncOpBarrier::begin: barrier already launched");
		items = [];
		// start an expecting coroutine for each action
		for (var i in opSet)
			items.push (new expector (opSet[i]));
	});

	this.itemsCompleted.implement (function ()
	{
		return nItemsCompleted;
	});

	this.itemsTotal.implement (function ()
	{
		return items? items.length : 0;
	});

	this.updateSignal.implement (function ()
	{
		return updateSignalObj;
	});

	this.mxOverride (MxAsyncOp,
	{
		$doCancel: function () { disposeItems (); }
	});
}catch(e){this.markToDispose(function(){throw e;});this.dispose();}});

//
// timer
//

/// Implements one-shot or periodic timer that posts a (static) event on tick
/// into appEQ. There is only one tick event object per timer, accessible by
/// tick () method. It won't be posted twice until dropped or consumed.
/// Also capable of measuring time intervals between now and manually set timing
/// mark.
/// Timer implements MxDisposable. Disposal stops the timer.
/// @tparam postPersistent Boolean If true, the timer will post its tick event
/// as persistent, otherwise it will post it normally.
  function Timer (postPersistent)
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var tick = this.markToDispose((makeStaticEvent ({ eventType: "timerTick" }))),
		timerID,
		oneShot,
		lastTime = new Date ();

	var kk = 0;
	function post ()
	{
		// helper function, posts the tick event if possible
		if (!tick.postedIn ())
		{
			if (postPersistent) appEQ.sendStaticPersistent (tick);
			else appEQ.sendStatic (tick);
		}
	}

	/// Starts the timer in one-shot mode (it will tick only once). Overrides
	/// current timer mode and resets current timing.
	/// @tparam Number delay Delay of the timer, in milliseconds
	this.startOneShot = function (delay)
	{
		Kbt.assert (!this.isDisposed (),"Timer: attempt to use after disposal");
		this.stop ();
		timerID = setTimeout (post,delay);
		lastTime = new Date ();
	};

	/// Starts the timer in normal (periodically ticking) mode. Overrides
	/// current timer mode and resets current timing.
	/// @tparam Number delay Delay of the timer, in milliseconds
	this.start = function (delay)
	{
		Kbt.assert (!this.isDisposed (),"Timer: attempt to use after disposal");
		this.stop ();
		timerID = setInterval (post,delay);
		lastTime = new Date ();
	};
	
	/// Stops the timer and withdraws its tick event possibly in queue.
	this.stop = function ()
	{
		Kbt.assert (!this.isDisposed (),"Timer: attempt to use after disposal");
		if (timerID)
			if (oneShot) clearTimeout (timerID);
			else clearInterval (timerID);
		timerID = undefined;
		tick.withdraw ();
	};

	/// Gets the tick event object. It implements MxStaticEvent, and its
	/// eventType is "timerTick". DO NOT DISPOSE!!!
	this.tick = function () { return tick; };

	/// Sets timing mark relative to which time will be returned by getTiming.
	/// Is automatically set on start, startOneShot and timer creation.
	this.resetTiming = function ()
	{
		Kbt.assert (!this.isDisposed (),"Timer: attempt to use after disposal");
		lastTime = new Date ();
	};

	/// Gets time elapsed since last resetTiming, start, startOneShot or
	/// creation of this timer.
	/// @tparam Number Time, in milliseconds
	this.getTiming = function ()
	{
		Kbt.assert (!this.isDisposed (),"Timer: attempt to use after disposal");
		return new Date ().getTime ()-lastTime.getTime ();
	};

	this.markToDispose(kbtBind(this,function(){ this.stop (); } // timer is automatically stopped on disposal
));}

/// Timer that ticks once per 30 ms, aka once per frame (~30 FPS), initialized
/// and shut down by main app function
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}var appFrameTimer = null;
var appLastFrameElapsed = 0; // ms elapsed per last frame tick


//
// graphutil.kbtjsx: coreutil.kbtjsx, document appOffscreenPool
//

// types:
// FontDesc (conventional):
// {
//  height: int, // in pixels
//  bold: bool,
//  italic: bool
// }
// Aligns (conventional):
// {
//  h: string (left|right|center),
//  v: string (top|center|middle|bottom), // center==middle
// }
// Subimage (conventional): { image: ImageData, srcRect: Rect, size: Size }

/// Saves state of gc (per gc.save) and return MxDisposable that restores it
/// on disposal. Therefore, assumed use is: #auto { #~(saveGCState (gc)); ... }
function saveGCState (gc)
{
	return new   function ()
	{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
		gc.save ();
		this.markToDispose(kbtBind(this,function(){ gc.restore (); }
	));} catch(e){this.markToDispose(function(){throw e;});this.dispose();}}();
}

var _$ossPool = new Kbt.List (), // offscreen surfaces pool
	_$ossTotal = 0;

/// Implements an offscreen surface. It's basically a hidden canvas on which
/// you can draw and which you can blit to main GC (appGC) via drawImage et al.
/// Is disposable, disposing surface object makes it no longer usable and
/// returns canvas into the pool. Number of surfaces is limited to browser's
/// memory.
/// @tparam Size size Size of desired surface
  function OffscreenSurface (size)
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	if (_$ossPool.count ()<=0)
	{
		// pool of existing canvases exhausted - add new canvas
		var newId = "bmcCanvas"+(_$ossTotal++);
		appOffscreenPool.innerHTML += Kbt.format (
			"\r\n\
<canvas width=%w% height=%h% id=%id% style=\"position: absolute\"></canvas>\r\n\
",
		{
			"w": size.width,
			"h": size.height,
			"id": newId
		});
		_$ossPool.addLast (document.getElementById (newId));
	}
	// get canvas from pool
	var canvas = _$ossPool.removeFirst ().value;
	[canvas.width, canvas.height] = [size.width, size.height];
	if (!canvas._$GC)
	{
		canvas._$GC = canvas.getContext ("2d");
		canvas._$GC.drawText = appGC.drawText;
		canvas._$GC.drawMultilineText = appGC.drawMultilineText;
		canvas._$GC.drawSubimage = appGC.drawSubimage;
	}
	var GC = canvas._$GC;

	/// Access to OffscreenSurface's canvas. You can change its size.
	/// @treturn HTMLCanvas Canvas of the offscreen surface
	this.canvas = function () { return canvas; };

	/// Access to OffscreenSurface's canvas' 2d context.
	/// @treturn HTMLContext Canvas of the offscreen surface
	this.GC = function () { return GC; };

	/// Get current surface size
	/// @treturn Size Surface size, in pixels
	this.getSize = function () { return new Size (canvas.width,canvas.height); };

	this.setSize = function (size)
	{
		[canvas.width, canvas.height] = [size.width, size.height];
	};

	this.markToDispose(kbtBind(this,function(){
		// on destruction...
		// minimize canvas memory consumption
		[canvas.width, canvas.height] = [0,0];
		// return it to the pool
		_$ossPool.addLast (canvas);
		// prevent further usage of the object
		canvas = null;
		this.canvas = this.GC = this.size = function ()
		{ Kbt.panic ("OffscreenSurface: used after disposed!"); };
	}

	// done with implanting mixins
	));this.mxFreeze ();
}

/// Implements a font. Simplifies specification of the font, measurement and
/// drawing of strings. For simplicity, font typeface is limited to sans-serif
/// only. Font object is disposable.
/// @tparam fontDesc FontDesc Font description
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}  function Font (fontDesc)
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var desc = [],
		height = fontDesc.height || 1,
		bold = !!fontDesc.bold,
		italic = !!fontDesc.italic;
	if (fontDesc.italic) desc.push ("italic");
	if (fontDesc.bold) desc.push ("800");
	else desc.push ("400");
	desc.push (height+"px");
	desc.push ("sans-serif");
	desc = desc.join (" ");

	/// Measure (single line) text rendered in this font
	/// @tparam String Text to measure
	/// @treturn Size Text size
	this.measure = function (text)
	{
		appGC.font = desc;
		return new Size (appGC.measureText (text).width,height);
	};

	/// Return copy of font descriptor used to create this font
	this.getDesc = function ()
	{
		return { height: height, bold: bold, italic: italic };
	};

	/// Set this font as current in a given GC
	this.setInGC = function (gc)
	{
		gc.font = desc;
	};
}

/// This function extends appGC and GC()s from OffscreenSurface objects
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}appGC.drawText = function (font,point,text,aligns)
{
	font.setInGC (this);
	this.textAlign = (aligns && aligns.h) || "left";
	this.textBaseline = (aligns && aligns.v) || "top";
	this.fillText (text,point.x,point.y);
};

/// This function extends appGC and GC()s from OffscreenSurface objects
appGC.drawMultilineText = function (multilineText,refRect,aligns)
{
	multilineText._$render (this,refRect,aligns);
};

appGC.drawSubimage = function (point,subimage)
{
	this.drawImage (subimage.image,
		subimage.srcRect.x,subimage.srcRect.y,
		subimage.srcRect.width,subimage.srcRect.height,
		point.x,point.y,
		subimage.size.width,subimage.size.height);
};

/// Implements a multiline word-wrapping text formatted to fit in a certain
/// width. Incapsulates calculation of size and arrangemens, also adds ability
/// to render it on appGC and GC()s of OffscreenSurface-s.
  function MultilineText ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var font = null,
		size = new Size (0,0),
		measuredSize = undefined,
		lineSpacing = 0,
		textChanged = true, // or font
		words = [], // [string+{ width: int, space: bool, cr: bool }]
		lines = null; // [{ width: int, y: int, text: text }]

	this.markToDispose(kbtBind(this,function(){
		do  { if (font) { (font).dispose (); font= null; } } while (0) ;
	}

	));function measureWords ()
	{
		if (textChanged && font)
		{let _Kbt$as=new _Kbt$AutoScope();try
		{
			_Kbt$as.markToDispose((saveGCState (appGC)));
			font.setInGC (appGC);
			for (var i in words)
			{
				var word = words[i];
				if (word.text===undefined)
					// words are raw strings after first tokenization, not yet
					// measured
					words[i] =
					{
						text: word,
						width: word=="\n"? 0 : appGC.measureText (word).width,
						space: word[0]==" ",
						cr: word=="\n"
					};
				else
					// words are remeasured
					word.width =
						word=="\n"? 0 : appGC.measureText (word.text).width;
			}
			textChanged = false;
		}
	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}

	function arrange ()
	{
		if (!measuredSize && font)
		{
			measureWords ();
			var i = 0,nWords = words.length,
				y = 0,
				hgt = font.getDesc ().height,
				wdt = size.width,
				lineText,lineWidth,maxWidth = 0;
			lines = [];
			for (; i<nWords;)
			{
				lineText = ""; lineWidth = 0;
				for (; i<nWords; i++)
				{
					var word = words[i];
					if (word.cr)
					{
						i++;
						lines.push ({
							text: lineText,
							width: lineWidth,
							y: y
						});
						y += hgt+lineSpacing;
						maxWidth = Math.max (maxWidth,lineWidth);
						lineText = ""; lineWidth = 0;
						break;
					}
					if (word.width+lineWidth>wdt)
					{
						if (lineWidth<=0 && !word.space)
						{
							// line has only started, first and only word
							lineText += word.text;
							lineWidth += word.width;
							i++;
						}
						// skip space occured in which line break occured
						if (i<nWords && words[i].space) i++;
						lines.push ({
							text: lineText,
							width: lineWidth,
							y: y
						});
						y += hgt+lineSpacing;
						maxWidth = Math.max (maxWidth,lineWidth);
						lineText = ""; lineWidth = 0;
						break;
					}
					else
					{
						lineText += word.text;
						lineWidth += word.width;
					}
				}
			}
			if (lineWidth>0)
			{
				lines.push ({
					text: lineText,
					width: lineWidth,
					y: y
				});
				maxWidth = Math.max (maxWidth,lineWidth);
			}
			measuredSize = new Size (maxWidth,lines.length*(hgt+lineSpacing));
		}
	}

	this.setText = function (text)
	{
		words = text.replace (/(\r\n|\n\r|\r|\n)/g,"\n").split (/(\n|[^\n\S]+)/);
		textChanged = true;
		measuredSize = undefined;
	};

	this.setLineSpacing = function (newLineSpacing)
	{
		lineSpacing = newLineSpacing;
		measuredSize = undefined;
	};

	this.getLineSpacing = function ()
	{
		return lineSpacing;
	};

	this.setFont = function (newFont)
	{
		if (!(newFont instanceof Font))
			// if newFont is not a ready font, assume font descriptor
			newFont = new Font (newFont);
		var nfont = newFont? newFont.retain () : null;
		do  { if (font) { (font).dispose (); font= null; } } while (0) ; // dispose after retaining previous,
		// for case if previous is same as new one
		font = newFont;
		textChanged = true;
		measuredSize = undefined;
	};

	this.setRefSize = function (newSize)
	{
		Kbt.assert (newSize instanceof Size,
			"MultilineText::setSize: parameter must be size");
		size = newSize;
		measuredSize = undefined;
	};

	this.getRefSize = function ()
	{
		return size;
	};

	/// Return actual size of the text's bounding box
	this.actualSize = function ()
	{
		arrange ();
		return measuredSize? measuredSize : new Size (0,0);
	};

	this._$render = function (gc,refRect,aligns)
	{
		arrange ();
		if (font)
		{let _Kbt$as=new _Kbt$AutoScope();try
		{
			_Kbt$as.markToDispose((saveGCState (gc)));
			font.setInGC (gc);
			var halign = (aligns && aligns.h) || "left",
				valign = (aligns && aligns.v) || "top",
				hgt = font.getDesc ().height;
			var xorig,yorig;
			switch (halign)
			{
				case "left":
				default:
					xorig = refRect.x; break;
				case "right":
					xorig = refRect.rightBottom ().x; break;
				case "center":
					xorig = refRect.center ().x; break;
			}
			switch (valign)
			{
				case "top":
					yorig = refRect.y; break;
				case "bottom":
					yorig = refRect.rightBottom ().y-
						lines.length*(hgt+lineSpacing);
					break;
				case "middle":
				case "center":
					yorig = refRect.center ().y-
						lines.length*(hgt+lineSpacing)*0.5;
					break;
			}
			gc.textAlign = halign;
			gc.textBaseline = "top";
			for each (var line in lines)
				gc.fillText (line.text,xorig,yorig+line.y);
		}
	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}};
}
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}


//
// resutil.kbtjsx: coordinator.kbtjsx, coreutil.kbtjsx
// Resource (mainly image) utils
//

/// Initiate image loading operation
/// @treturn [MxAsyncOp,Image] Tuple of loading async op and image to load
function beginLoadImage (src)
{
	var asyncOp = new MxAsyncOp (),
		img = new Image ();
	img.src = src;
	img.onload = function ()
	{
		img.onload = null;
		if (typeof (mainInstance)!="undefined")
			asyncOp.$completed ();		
	};
	asyncOp.src = src;
	return [asyncOp,img];
}

/// Initiate loading of images in 'images' object (see resources.kbtjsx) and set
/// them to signal on given barrier. The barrier will be retained (per
/// MxDisposable::retain) for an asynchronous operation.
function beginLoadImages (barrier)
{
	var ops = [];
	for (var img in images)
	{
		var loadedOp;
		// initiate loading (it will be done via one of begin... functions
		// implemented earlier in this file)
		[loadedOp,images[img]] = images[img] ();
		// add operation to list
		ops.push (loadedOp);
	}

	// launch auxiliary expecting coroutine (its purpose is to dispose
	// operations from ops)
	new function ()
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func,["imagesLoaded"])));
		var eLoaded = _Kbt$as.markToDispose((makeAsyncSignal ({ eventType: "imagesLoaded" }))),
			eLoadedSink;
		_Kbt$as.markToDispose((barrier.retain ())); // retain the barrier for this coroutine
		barrier.begin (ops);
		eLoaded.asgWaitFor (barrier);
		do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[eLoadedSink] = [_Kbt$msg];_Kbt$consumed = true; if(eLoadedSink==eLoaded){;break;}else _Kbt$consumed = false;}} while (false);
		disposeArray (ops);
	} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
}


//
// guicore.kbtjsx: graphutil.kbtjsx
// Provides core of a GUI (mixin MxWidget)
//

// Basic widget. Is base for all widget classes, can also be used by itself as
// a basic widget container with no background.
var MxWidget = Kbt.Mixin.declare ([MxDisposable],
{
	/// Get widget's current children collection
	getChildren: Kbt.Func.placeholder, // () => ImxEnumerable

	/// Adds widget as a child to given one. Child widgets are clipped to parent
	/// (unless no-clipping mode is specified), have their coordinates specified
	/// relative to parent's top left corner, are rendered after parent's
	/// background and before its foreground and are offered point messages
	/// before parent.
	/// If the widget is currently child of another parent, it is removed from
	/// that before assignment to new one. Parent may be set to null, the widget
	/// becomes independent ('root') in this case.
	/// @tparam MxWidget parent Widget to set as this widget's parent. Must be
	/// a MxWidget enabled object or null.
	setParent: Kbt.Func.placeholder, // (MxWidget parent)

	/// Get widget's current parent (null by default)
	getParent: Kbt.Func.placeholder, // () => MxWidget or null

	/// Set the widget as sibling to a given one, following before it in
	/// siblings list. Order of siblings determines order of their rendering
	/// (first to last) and order of offering point events (last to first) if
	/// they overlap.
	/// @tparam MxWidget sibling Sibling to place this widget before. Widget's
	/// new parent is that of sibling.
	addBeforeSibling: Kbt.Func.placeholder, // (MxWidget sibling)

	/// Set the widget as sibling to a given one, following after it in
	/// siblings list.
	/// @tparam MxWidget sibling Sibling to place this widget after. Widget's
	/// new parent is that of sibling.
	addAfterSibling: Kbt.Func.placeholder, // (MxWidget sibling)

	/// Enable (param==true) or disable (param==false) bitmap caching of the
	/// widget. If enabled, this widget is painted on an offscreen surface which
	/// is then used whenever possible until the widget is invalidated.
	enableBitmapCache: Kbt.Func.placeholder, // (Boolean)

	/// Enable (param==true) or disable (param==false) clipping of drawing (both
	/// self and children) to the widget's rectangle. Default is true.
	/// If bitmap caching is enabled, clipping children is always done
	/// regardless on setting specified by enableChildClipping.
	enableClipping: Kbt.Func.placeholder, // (Boolean)

	/// Set widget's rectangle in coordinates relative to parent. For root
	/// widget, coordinates are absolute.
	setRect: Kbt.Func.placeholder, // (Rect)

	/// Get widget's rectangle in coordinates relative to parent
	getRect: Kbt.Func.placeholder, // () => Rect

	/// Get widget's rectangle in its own local coordinates (i. e., with (0,0)
	/// as top left corner). Useful for comparing against coordinates of point
	/// messages.
	getRectLocal: Kbt.Func.placeholder, // () => Rect

	/// Get widget's rectangle in coordinates relative to parent. For root
	/// widget, coordinates are absolute.
	getRectAbs: Kbt.Func.placeholder, // () => Rect

	/// Convert point coordinates from local to this widget to absolute
	pointToAbs: Kbt.Func.placeholder, // (Point pointRel) => Point

	/// Convert point coordinates from absolute to local to this widget
	pointFromAbs: Kbt.Func.placeholder, // (Point pointAbs) => Point

	/// Set widget's visibility. If false, the widget and its descendants are
	/// not rendered and are not offered point events.
	setVisible: Kbt.Func.placeholder, // (Boolean)

	/// Get widget's visibility set by setVisible. Does not necessarily match
	/// widget's actual visibility (it may be invisible due to invisibility of
	/// an ancestor). To get actual visibility, use getVisibleAbs.
	getVisible: Kbt.Func.placeholder, // () => Boolean

	/// Get widget's actual visibility.
	getVisibleAbs: Kbt.Func.placeholder, // () => Boolean

	/// Set widget's opacity. Only has meaning if the widget is bitmap cached.
	/// Only affects rendering. Opacity of 0 is not equivalent to
	/// setVisible (false), the widget will still be offered point events if
	/// they hit its rectangle.
	/// @tparam Number opacity Widget's opacity, clamped to 0 to 1
	setOpacity: Kbt.Func.placeholder, // (Number opacity)

	/// Set widget's opacity. Only has meaning if the widget is bitmap cached.
	/// @treturn Number Widget's opacity. Default is 1.
	getOpacity: Kbt.Func.placeholder, // () => Number

	/// Set offset of the widget's viewport. Its top left corner will correspond
	/// to local coordinates 'offset'. You may use to implement scrolling areas.
	/// @tparam Point offset The viewport offset
	setViewportOffset: Kbt.Func.placeholder, // (Point offset)

	/// Get current viewport offset of the widget.
	/// @treturn Point The viewport offset
	getViewportOffset: Kbt.Func.placeholder, // () => Point

	/// Offer point message at point pointRel, coordinates local to this widget.
	/// It's first routed 'screenwards' (to frontmost child whose rectangle is
	/// hit by given point), then 'userwards' (back to ancestors).
	/// Use this when translating a screenwards message to a widget into
	/// conventional screenwards messages to some other container.
	/// @tparam Point pointRel Coordinates associated with the message, relative
	/// to widget
	/// @treturn MxWidget Widget that accepted the message, null if none
	sendPointMessage: Kbt.Func.placeholder, // (Point pointRel,message) => MxWidget or null

	/// Offer point message at point pointRel, coordinates local to this widget.
	/// It's offered to this widget first, then upwards to ancestors.
	/// Use it to offer message to a widget that is logically opaque to
	/// screenwards messages. You can also use it to a emit message from the
	/// widget itself userwards (call offerPointMessage of the widget itself).
	/// @treturn MxWidget Widget that accepted the message, null if none
	offerPointMessage: Kbt.Func.placeholder, // (Point pointRel,message) => MxWidget or null

	/// Draw the widget on a given GC. Widget's top left corner is assumed to
	/// correspond to GC's (0,0). Use transforms to change how does the widget
	/// is transformed on rendering. Only use on root widgets.
	/// @tparam CanvasRenderingContext2D gc 2D context to render on
	draw: Kbt.Func.placeholder, // (GC gc)

	/// Mark widget's visual state as invalidated. If widget or one of its
	/// ancestors use caching, the cache becomes invalidated and will be
	/// repainted on soonest occasion. Always call if a widget's visual state
	/// is assumed to havee changed.
	invalidate: Kbt.Func.placeholder, // ()

	/// Is invoked when widget is painted, before its children are painted. Use
	/// to render your widget's background.
	/// If your widget inherits some other one and overrides this function,
	/// you generally should include call to parent's onDrawBackground before
	/// doing your own draw.
	onDrawBackground: Kbt.Func.virtual (function () {}),
	// (GC gc,Point leftTopInGC)

	/// Is invoked when widget is painted, after its children are painted. Use
	/// to render your widget's foreground.
	/// If your widget inherits some other one and overrides this function,
	/// you generally should include call to parent's onDrawForeground after
	/// doing your own draw.
	onDrawForeground: Kbt.Func.virtual (function () {}),
	// (GC gc,Point leftTopInGC)

	/// Is invoked when the widget is offered point event. Should decode the
	/// message, take appropriate actions (if any needed) and return true if the
	/// message is consumed or false if it's ignored. Ignoring message does not
	/// assume that you must do nothing in response to it (though it's mostly
	/// the case), it just means that message will be routed further to
	/// ancestors. If the message is consumed, it's not routed further, and this
	/// widget is returned as message consumer by offerPointMessage/
	/// sendPointMessage that dispatched this message.
	/// If your widget inherits some other one and overrides this function,
	/// you generally should include call to parent's onPointMessage somewhere
	/// in your overriding onPointMessage, so that the widget inherited
	/// consistent reaction to UI events.
	/// @tparam
	/// @treturn Boolean true if the message is consumed and must not be offered
	/// to ancestors, false otherwise
	onPointMessage: Kbt.Func.virtual (function () { return false; }),
	// (Point pointRel,message) => Boolean

	/// Is invoked when parent-local rectangle of the widget is changed.
	/// @tparam Rect newRectAbs New rectangle in parent-local coordinates
	/// @tparam Rect prevRectAbs Previous rectangle in parent-local coordinates
	onRectChanged: Kbt.Func.virtual (function () {}),
	// (Rect newRect,Rect prevRect)

	/// Is invoked when widget's own visibility is changed via setVisible. Is
	/// not invoked if widget's visibility changes because of its ancestors'
	/// visibility changes, or when the new visibility is same as current.
	/// @tparam Boolean newVisibility New visibility (previous one can always
	/// be assumed opposite to new)
	onVisibilityChanged: Kbt.Func.virtual (function () {}),
	// (bool newVisibility)

	/// Is invoked when parent is changed
	/// @tparam MxWidget newParent New parent (or null if widget is detached)
	/// @tparam MxWidget prevParent Previous parent (or null if widget was
	/// detached)
	onParentChanged: Kbt.Func.virtual (function () {}),
	// (MxWidget newParent,MxWidget prevParent)

	/// Is invoked when viewport offset is changed
	/// @tparam Point newViewportOffset New viewport offset
	/// @tparam Point prevViewportOffset Previous viewport offset
	onViewportOffsetChanged: Kbt.Func.virtual (function () {})
	// (Point newViewportOffset,Point prevViewportOffset)
},
  function ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var me = this.mxRoot ()._$MxWt =
	{
		myself: this,
		children: new Kbt.List (),
		parent: null, // null or MxWidget
		rect: new Rect (0,0,0,0),
		viewportOffset: new Point (0,0),
		visible: true,
		clippingEnabled: true,
		bitmapCache: null, // null or OffscreenSurface
		invalidated: true,
		opacity: 1,
		sibLink: new Kbt.ListNode (this.mxRoot ())
	},
	disposing = false;

	this.markToDispose(kbtBind(this,function(){
		disposing = true;
		if (me.bitmapCache!=null) me.bitmapCache.dispose ();
		this.setParent (null);
	}

	));this.getChildren.implement (function ()
	{
		return ImxEnumerable.dynamicCast (me.children);
	});

	function addToParent (parent,sibling,beforeSibling)
	{
		if (sibling!=null)
		{
			parent = sibling.mxRoot ()._$MxWt.parent;
			Kbt.assert (parent,
				"MxWidget::addBeforeSibling/addAfterSibling - sibling must have parent");
		}
		Kbt.assert (parent==null ||
			(parent=MxWidget.dynamicCast (parent)),
			"MxWidget::setParent - parent must implement MxWidget or be null");
		var prevParent = me.parent;
		if (prevParent!=null)
		{
			// detach from previous parent
			var pp = prevParent.mxRoot ()._$MxWt;
			pp.children.remove (me.sibLink);
			prevParent.invalidate ();
		}

		if (!parent)
			me.parent = null;
		else
		{
			var pp = parent.mxRoot ()._$MxWt;
			if (sibling)
				if (beforeSibling)
					pp.children.addBefore (sibling._$MxWt.sibLink,me.sibLink);
				else
					pp.children.addAfter (sibling._$MxWt.sibLink,me.sibLink);
			else pp.children.addLast (me.sibLink);
			me.parent = parent;
			parent.invalidate ();
		}

		if (!disposing)
			// invoke parent changing notification if appropriate
			me.myself.onParentChanged (parent,prevParent);
	}

	this.setParent.implement (function (parent)
	{
		Kbt.assert (parent==null ||
			(parent=MxWidget.dynamicCast (parent)),
			"MxWidget::setParent - parent must implement MxWidget or be null");
		addToParent (parent,null);
	});

	this.getParent.implement (function ()
	{
		return me.parent;
	});

	this.addBeforeSibling.implement (function (sibling)
	{
		Kbt.assert (sibling = MxWidget.dynamicCast (sibling),
			"MxWidget::addBeforeSibling: sibling must be a non-null MxWidget implementor");
		addToParent (undefined,sibling,true);
	});

	this.addAfterSibling.implement (function (sibling)
	{
		Kbt.assert (sibling = MxWidget.dynamicCast (sibling),
			"MxWidget::addBeforeSibling: sibling must be a non-null MxWidget implementor");
		addToParent (undefined,sibling,false);
	});

	this.enableBitmapCache.implement (function (yes)
	{
		Kbt.assert (!this.isDisposed (),"MxWidget: can't use disposed widget");
		yes = yes===undefined || yes;
		if (!yes && me.bitmapCache)
		{
			me.bitmapCache.dispose ();
			me.bitmapCache = null;
		}
		if (yes && !me.bitmapCache)
			me.bitmapCache = new OffscreenSurface (me.rect.size ());
		if (yes)
		{
			me.bitmapCache.setSize (me.rect.size ());
			this.invalidate ();
		}
	});

	this.enableClipping.implement (function (yes)
	{
		if (me.clippingEnabled!=yes)
		{
			me.clippingEnabled = yes;
			this.invalidate ();
		}
	});

	me.doDraw = function (gc,offset)
	{
		var me = this; // invoked in such context
		// offset is Point
		if (!me.visible) return; // not visible
		{let _Kbt$as=new _Kbt$AutoScope();try
		{
			_Kbt$as.markToDispose((saveGCState (gc)));
			// set clipping if required
			var clip = me.clippingEnabled || me.bitmapCache!=null;
			if (me.clippingEnabled)
			{
				gc.beginPath ();
				var myRectSize = me.rect.size ();
				gc.rect (offset.x,offset.y,myRectSize.width,myRectSize.height);
				gc.clip ();
			}

			// render background
			me.myself.onDrawBackground (gc,offset);

			// render children
			{let _Kbt$as=new _Kbt$AutoScope();try {
				_Kbt$as.markToDispose((saveGCState (gc)));
				var
					myActualRect = Rect.fromPointAndSize (offset,me.rect.size ()),
					childrenOffset = me.viewportOffset.neg (),
					defOpacity = gc.globalAlpha,
					opacity = 1;
				for (var child in me.children.elements ())
				{
					var childDetails = child.mxRoot ()._$MxWt,
						childActualRect = childDetails.rect
							.add (childrenOffset).add (offset);
					if (clip &&
						childActualRect.intersection (myActualRect).isEmpty ())
						continue; // child is entirely clipped out
					if (childDetails.bitmapCache!=null)
					{
						// child uses bitmap cache
						if (childDetails.invalidated)
						{
							// cache is invalidated - repaint it
							childDetails.bitmapCache.GC ().clearRect
								(0,0,childDetails.rect.width,childDetails.rect.height);
							childDetails.doDraw (childDetails.bitmapCache.GC (),new Point (0,0));
							childDetails.invalidated = false;
						}
						// adjust opacity
						if (opacity!=childDetails.opacity)
						{
							opacity = childDetails.opacity;
							gc.globalAlpha = opacity*defOpacity;
						}
						// blit the cache
						gc.drawImage (childDetails.bitmapCache.canvas (),
							childActualRect.x,childActualRect.y);
					}
					else
					{
						// child doesn't use bitmap cache - draw directly
						// (always at full opacity)
						if (opacity!=1)
						{
							opacity = 1;
							gc.globalAlpha = defOpacity;
						}
						childDetails.doDraw (gc,childActualRect.leftTop ());
					}
				}
			}	

			// render foreground
			catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}me.myself.onDrawForeground (gc,offset);
		}
	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}};

	this.draw.implement (function (gc)
	{
		{let _Kbt$as=new _Kbt$AutoScope();try
		{
			_Kbt$as.markToDispose((saveGCState (gc)));
			var offset = me.rect.leftTop ();
			if (me.bitmapCache!=null)
			{
				// use bitmap cache
				if (me.invalidated)
				{
					me.bitmapCache.GC ().clearRect
						(0,0,me.rect.width,me.rect.height);
					me.doDraw (me.bitmapCache.GC (),new Point (0,0));
					me.invalidated = false;
				}
				gc.globalAlpha = me.opacity*gc.globalAlpha;
				// blit the cache
				gc.drawImage (me.bitmapCache.canvas (),offset.x,offset.y);
			}
			else
				// no bitmap cache, draw directly
				me.doDraw (gc,new Point (offset.x,offset.y));
		}
	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}});

	this.invalidate.implement (function ()
	{
		for (var pWt=me;
			pWt!=null;
			pWt=pWt.parent && pWt.parent.mxRoot ()._$MxWt)
			pWt.invalidated = true;
	});

	this.setRect.implement (function (rect)
	{
		var prevRect = me.rect;
		me.rect = rect;
		this.onRectChanged (rect,prevRect);
		if (!rect.equal (prevRect))
		{
			if (me.bitmapCache) me.bitmapCache.setSize (rect.size ());
			this.invalidate ();
		}
	});

	this.getRect.implement (function ()
	{
		return me.rect;
	});

	this.getRectLocal.implement (function ()
	{
		return new Rect (0,0,me.rect.width,me.rect.height);
	});

	this.getRectAbs.implement (function ()
	{
		var rect = me.rect;
		for (var wt=me.parent; wt!=null;)
		{
			var meWt = wt.mxRoot ()._$MxWt;
			rect = rect.sub (meWt.viewportOffset)
				.add (meWt.rect.leftTop ());
			wt = meWt.parent;
		}
		return rect;
	});

	this.pointToAbs.implement (function (pointRel)
	{
		return pointRel.add (this.getRectAbs ().leftTop ());
	});

	this.pointFromAbs.implement (function (pointAbs)
	{
		return pointAbs.sub (this.getRectAbs ().leftTop ());
	});

	this.setVisible.implement (function (visible)
	{
		if (me.visible!=visible)
		{
			me.visible = visible;
			me.visibleAbs = undefined;
			this.onVisibilityChanged (visible);
			this.invalidate ();
		}
	});

	this.getVisible.implement (function ()
	{
		return me.visible;
	});

	this.getVisibleAbs.implement (function ()
	{
		var visible = me.visible;
		for (var wt=me.parent; wt!=null;)
		{
			var meWt = wt.mxRoot ()._$MxWt;
			visible &= meWt.visible;
			wt = meWt.parent;
		}
		return visible;
	});

	this.setOpacity.implement (function (opacity)
	{
		me.opacity = opacity;
		if (me.opacity<0) me.opacity = 0;
		if (me.opacity>1) me.opacity = 1;
		if (me.bitmapCache!=null && me.parent) me.parent.invalidate ();
	});

	this.getOpacity.implement (function ()
	{
		return me.opacity;
	});

	this.setViewportOffset.implement (function (offset)
	{
		var prevOffset = me.viewportOffset;
		me.viewportOffset = offset;
		this.onViewportOffsetChanged (offset,prevOffset);
	});

	this.getViewportOffset.implement (function ()
	{
		return me.viewportOffset;
	});

	this.sendPointMessage.implement (function (pointRel,message)
	{
		var myRect = Rect.fromPointAndSize (new Point (0,0),me.rect.size ()),
			viewportOffset = me.viewportOffset;
		// if the event is not outside clipping rect (or clipping is disabled),
		// offer it to children first
		if ((!me.clippingEnabled && me.bitmapCache==null) ||
			myRect.isInside (pointRel))
		{
			var cldAccepted = null;
			for (var child in me.children.elementsReverse ())
			{
				child = child.mxRoot ()._$MxWt;
				if (!child.visible)
					continue; // don't offer to invisible children
				var childRect = child.rect.add (myRect.leftTop ())
					.sub (me.viewportOffset);
				if ((child.clippingEnabled || child.bitmapCache!=null) &&
					!childRect.isInside (pointRel))
					continue; // message doesn't hit a child with clipping
				if (cldAccepted = child.myself.sendPointMessage
					(pointRel.sub (childRect.leftTop ()),message))
					break; // child (or some of its descendants) has accepted
			}
			if (cldAccepted) return cldAccepted;
		}
		// no children have accepted the message, or the message hit outside
		// clipping area and should only be offered to self
		return this.offerPointMessage (pointRel,message);
	});

	this.offerPointMessage.implement (function (pointRel,message)
	{
		for (var wt=this;;)
		{
			if (wt.onPointMessage (pointRel,message))
				return wt; // self (or ancestor) accepted the message

			// offer to next ancestor
			var me = wt.mxRoot ()._$MxWt,
				parent = me.parent;
			if (parent==null) break;
			// adjust point coords to parent-local
			pointRel = pointRel.add (me.rect.leftTop ())
				.sub (parent.mxRoot ()._$MxWt.viewportOffset);
			wt = parent;
		}
		return null; // none of self or ancestors accepted the message
	});
}catch(e){this.markToDispose(function(){throw e;});this.dispose();}});

/// Host for a GUI that translates raw mouseDown/Move/Up events to
/// touchDown/Move/Up/Reset of appropriate children, with respect to touch
/// capture rules (if a widget accepted touchDown, it captures all subsequent
/// touchMove-s until next touchUp/Reset, and that touchUp/Reset as well).
/// When GUI on your screen, use WtGuiHost as root widget for all other widgets,
/// dispatch mouseDown/Move/Up to it via dispatchMouseMessage and render it on
/// target GC via draw.
/// It can also be a good idea to use a separate WtGuiHost for a standalone
/// piece of GUI, such as a popup.
/// Underlying widgets are offered messages of following .eventType-s:
/// touchDown, touchUp, touchMove, touchReset (first three of obvious meaning,
/// touchReset is programmatic reset that occurs due to internal logic flow
/// instead of user action, it's assumed to be treated like touchUp but without
/// triggering click). All these messages contain extra member, .host, which
/// contains the controlling WtGuiHost. Widget that reacts touch events can
/// cache it and use later to reset the touch via dispatchMouseReset.
  function WtGuiHost ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	Kbt.Mixin.enable (this);
	this.mxEnable (MxWidget);

	var meAsWidget = this,
		capturer = null;

	var dispatcher = this.markToDispose((new function ()
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		var mouseDown,mouseMove,mouseUp;
		var touchDown = false;
		for (;;)
		{
			// assumed state: touch is up
			do {
				for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[mouseDown,mouseMove,mouseUp] = [_Kbt$msg,_Kbt$msg,_Kbt$msg];_Kbt$consumed = true; if(mouseDown.eventType=="mouseDown") {[mouseMove,mouseUp] = [undefined,undefined];break;}else 
				 if(mouseMove.eventType=="mouseMove") {[mouseDown,mouseUp] = [undefined,undefined];break;}else 
				 if(mouseUp.eventType=="mouseUp"){[mouseDown,mouseMove] = [undefined,undefined];break;}else _Kbt$consumed = false;}} while (false);
			if (mouseMove)
			{
				  _Kbt$reply = (meAsWidget.sendPointMessage (
					meAsWidget.pointFromAbs (new Point (mouseMove.X,mouseMove.Y)),
					{ eventType: "touchMove", host: meAsWidget })!=null);
				continue;
			}
			if (mouseDown)
			{
				// here begins the tricky part...
				touchDown = true;
				capturer = MxWidget.dynamicCast (meAsWidget.sendPointMessage (
					meAsWidget.pointFromAbs (new Point (mouseDown.X,mouseDown.Y)),
					{ eventType: "touchDown", host: meAsWidget }));
				if (capturer)
				{let _Kbt$as=new _Kbt$AutoScope();try
				{
					// someone captured the touch
					var lastPoint = // in absolute coords
						new Point (mouseDown.X,mouseDown.Y);
					_Kbt$as.markToDispose(kbtBind(this,function(){ capturer = null; }
					));  _Kbt$reply = true;
					for (;;)
					{
						do {
							for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[mouseMove,mouseUp,mouseReset] = [_Kbt$msg,_Kbt$msg,_Kbt$msg];_Kbt$consumed = true; if(mouseMove.eventType=="mouseMove") {[mouseUp,mouseReset] = [undefined,undefined];break;}else 
							 if(mouseUp.eventType=="mouseUp") {[mouseMove,mouseReset] = [undefined,undefined];break;}else 
							 if(mouseReset.eventType=="mouseReset"){[mouseMove,mouseUp] = [undefined,undefined];break;}else _Kbt$consumed = false;}} while (false);
						var message = undefined,
							newPoint;
						if (!capturer.isDisposed ())
						{
							if (mouseMove)
							{
								message =
									{ eventType: "touchMove", host: meAsWidget };
								newPoint = 
									new Point (mouseMove.X,mouseMove.Y);
							}
							else if (mouseUp)
							{
								message =
									{ eventType: "touchUp", host: meAsWidget };
								newPoint =
									new Point (mouseUp.X,mouseUp.Y);
							}
							else if (mouseReset)
							{
								message =
									{ eventType: "touchReset", host: meAsWidget };
								newPoint = lastPoint;
							}
							// send message to capturer (if it's still alive)
							  _Kbt$reply = (capturer.offerPointMessage (
								capturer.pointFromAbs (newPoint),message)!=null);
						}
						else   _Kbt$reply = false;
						if (mouseUp || mouseReset)
							break; // mouse raised or reset - end of capture
					}
					continue;
				}
				catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}else   _Kbt$reply = false;
			}
			if (mouseUp && touchDown)
			{
				touchDown = false;
				  _Kbt$reply = (meAsWidget.sendPointMessage (
					new Point (mouseUp.X,mouseUp.Y),
					{ eventType: "touchUp", host: meAsWidget })!=null);
				continue;
			}
		}
	}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}));

	/// Send mouse down/up/move message to the GUI host. Returns true if someone
	/// consumed the message, false otherwise. Message is assumed to contain
	/// coordinates X and Y in absolute coordinates.
	this.dispatchMouseMessage = function (message)
	{
		switch (message.eventType)
		{
		case "mouseDown":
		case "mouseMove":
		case "mouseUp":
			dispatcher (message);
			return dispatcher.reply;
		}
		return false;
	};

	/// Indicate that current mouse capture must be programatically reset
	/// (sends touchReset message to current capturer, if any).
	this.dispatchMouseReset = function ()
	{
		dispatcher ({ eventType: "mouseReset" });
	};

	/// Returns widget that currently is capturing the touch events (null if
	/// none). When comparing against this value for identity, use
	/// MxWidget.dynamicCast on value you want to compare.
	this.capturer = function ()
	{
		return capturer;
	};

	this.mxFreeze ();
}
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}


//
// basicwidgets.kbtjsx: guicore.kbtjsx
// Provides some core widgets (widgets WtLabel, WtImageClip, WtSolidColor,
// mixins MxWtProgressBar and MxWtBasicButton)
//

// type:
// Subimage (conventional): { image: ImageData, srcRect: Rect, size: Size }

/// Implements a plain solid color filled rectangle. Default color is black.
  function WtSolidColor ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var color = "#000000";
	this.mxEnable (MxWidget);

	/// Set color for the rectangle
	/// @tparam String newColor Color (in format of "#RRGGBB" or by web color
	/// name)
	this.setColor = function (newColor)
	{
		color = newColor;
		this.invalidate ();
	};

	var base = this.mxOverride (MxWidget,
	{
		onDrawBackground: function (gc,leftTop)
		{
			var rect = Rect.fromPointAndSize (leftTop,this.getRect ().size ());
			gc.fillStyle = color;
			gc.fillRect (rect.x,rect.y,rect.width,rect.height);
		}
	});

	this.mxFreeze ();
}

/// Implements single- or multiline plain text rendered in a given color,
/// formatting, font, alignment, line spacing and, optionally, auto-height
/// adjustment
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}  function WtLabel ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var mText = this.markToDispose((new MultilineText ())),
		autoLines = 1,
		fixedFontHeight = 15,
		fontDesc = { height: 0, bold: false, italic: false },
		font = null,
		aligns = { h: "left", v: "top" },
		color = "#000000",
		formatted = false,
		me = this;

	this.markToDispose(kbtBind(this,function(){ do  { if (font) { (font).dispose (); font= null; } } while (0) ; }

	));this.mxEnable (MxWidget);

	function adjustHeight ()
	{
		if (autoLines>0)
			fontDesc.height =
			(me.getRect ().size ().height-mText.getLineSpacing ()*(autoLines-1))
			/autoLines;
		else
			fontDesc.height = fixedFontHeight;
		do  { if (font) { (font).dispose (); font= null; } } while (0) ;
		font = new Font (fontDesc);
		mText.setFont (font);
		formatted = false;
	}

	function reformat (width,height)
	{
		if (!formatted)
		{
			mText.setRefSize (new Size (autoLines==1? Infinity : width,height));
			formatted = true;
		}
	}

	/// Set description for the font to use in this label
	/// @tparam FontDesc newFontDesc Font description. Height is only used in
	/// autoLines = 0 (see setAutoLines).
	this.setFontDesc = function (newFontDesc)
	{
		fontDesc = newFontDesc;
		fixedFontHeight = newFontDesc.height || 0;
		adjustHeight ();
		this.invalidate ();
	};

	/// Set text to display in this label
	/// @tparam String newText The text
	this.setText = function (newText)
	{
		mText.setText (newText);
		formatted = false;
		this.invalidate ();
	};

	/// Set number of lines in this widget. If set to non-zero, height of the
	/// font will accomodate so that the label rectangle comprised exacly this
	/// number of lines. If set to 0, fixed height specified in last setFontDesc
	/// will be used. If set to 1, the text will be forced to single line,
	/// except for explicit line breaks on "\n" characters.
	/// @tparam Number newAutoLines Auto lines count
	this.setAutoLines = function (newAutoLines)
	{
		Kbt.assert (autoLines>=0,
			"WtLabel::setAutoLines: must be non-negative");
		autoLines = newAutoLines;
		adjustHeight ();
		this.invalidate ();
	};

	/// Set vertical and horizontal alignments for the text. Widget's rectangle
	/// is the reference rect to align against (i. e., "right" means "right of
	/// the rectangle", "center" means "center of the rectangle", etc.).
	/// @tparam Aligns newAligns Alignments. Default is { h: "left", v: "top" }
	this.setAligns = function (newAligns)
	{
		aligns = newAligns;
		this.invalidate ();
	};

	/// Set color for the text
	/// @tparam String newColor Color (in format of "#RRGGBB" or by web color
	/// name)
	this.setColor = function (newColor)
	{
		color = newColor;
		this.invalidate ();
	};

	/// Set spacing between lines of multiline text (in pixels), default is 0
	/// @tparam Number lineSpacing Line spacing, in pixels
	this.setLineSpacing = function (lineSpacing)
	{
		mText.setLineSpacing (lineSpacing);
		formatted = false;
		this.invalidate ();
	};

	/// Get size of bounding box for currently formatted text. Its meaning is
	/// that changing widget's size to this size will exactly retain existing
	/// text formatting and will have exact size to display it entirely. The
	/// size is often different from widget's actual size.
	this.actualTextSize = function ()
	{
		return mText.actualSize ();
	};

	var base = this.mxOverride (MxWidget,
	{
		onDrawBackground: function (gc,leftTop)
		{
			base.onDrawBackground (gc,leftTop);

			var rect = Rect.fromPointAndSize (leftTop,this.getRect ().size ());
			reformat (rect.width,rect.height);
			gc.fillStyle = color;
			gc.drawMultilineText (mText,rect,aligns);
		},
		onRectChanged: function (newRect,prevRect)
		{
			base.onRectChanged (newRect,prevRect);
			adjustHeight ();
			if (!newRect.size ().equal (prevRect.size ()))
			{
				formatted = false;
				reformat (newRect.width,newRect.height);
			}
		}
	});

	this.mxFreeze ();
}

/// Implements image or a piece of image displayed with given stretch and
/// alignment
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}  function WtImageClip ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var
		subimage = null, // Subimage
		aligns = { h: "left", v: "top" },
		stretchMode = "none";

	this.mxEnable (MxWidget);

	/// Sets data (Subimage) to display
	/// @tparam Subimage newSubimage Subimage to display, null to display
	/// nothing
	this.setImageClip = function (newSubimage)
	{
		subimage = newSubimage;
		this.invalidate ();
	};

	/// Sets stretch mode. "none" (also default) is when (sub)image is shown
	/// without any stretching in its specified size. "fill" means that image
	/// will be stretched to exactly fit widget's rectangle, with aspect ratio
	/// not preserved. "uniform" means that image will be uniformely scaled so
	/// that its maximum dimension equaled minimum dimension of widget's rect,
	/// so that it will have maximum size it can with current aspect ratio while
	/// still entirely visible within widget's rect.
	/// "uniformtofill" means that image will be uniformely scaled so that its
	/// minimum dimension equaled maximum dimension of widget's rect, so that it
	/// had minimum size it covers widget's rect entirely with current aspect
	/// ratio.
	/// @tparam String newStretchMode Stretch mode: "none", "fill", "uniform" or
	/// "uniformtofill"
	this.setStretchMode = function (newStretchMode)
	{
		stretchMode = newStretchMode;
		this.invalidate ();
	};

	/// Sets aligmnents for the image against widget's rectangle
	/// @tparam Aligns newAligns
	this.setAligns = function (newAligns)
	{
		aligns = newAligns;
		this.invalidate ();
	};

	this.mxOverride (MxWidget,
	{
		onDrawBackground: function (gc,leftTop)
		{
			if (!subimage) return;
			var rect = Rect.fromPointAndSize (leftTop,this.getRect ().size ());
			if (rect.width<=0 || rect.height<=0) return;
			var dx,dy,dh,dw,
				sh = subimage.srcRect.height,
				sw = subimage.srcRect.width;
			switch (stretchMode)
			{
			default:
			case "none":
				dh = subimage.size.height;
				dw = subimage.size.width;
				break;
			case "uniform":
			case "uniformtofill":
				var dh1 = rect.height,
					dw1 = subimage.size.width*rect.height/subimage.size.height,
					dh2 = subimage.size.height*rect.width/subimage.size.width,
					dw2 = rect.width;
				if (dh1>rect.height || dw1>rect.width)
					[dh,dw] = stretchMode=="uniform"? [dh2,dw2] : [dh1,dw1];
				else
					[dh,dw] = stretchMode=="uniform"? [dh1,dw1] : [dh2,dw2];
				break;
			case "fill":
				dh = rect.height;
				dw = rect.width;
				break;
			}
			// calculate position to horiz alignment
			switch ((aligns && aligns.h) || "left")
			{
			case "left":
			default:
				dx = rect.x; break;
			case "center":
				dx = rect.x+(rect.width-dw)*0.5; break;
			case "right":
				dx = rect.x+rect.width-dw; break;
			}
			// calculate position to vert alignment
			switch ((aligns && aligns.v) || "top")
			{
			case "top":
			default:
				dy = rect.y; break;
			case "center":
			case "middle":
				dy = rect.y+(rect.height-dh)*0.5; break;
			case "bottom":
				dy = rect.y+rect.height-dh; break;
			}
			// and, finally, draw it!!
			gc.drawImage (subimage.image,
				subimage.srcRect.x,subimage.srcRect.y,
				sw,sh,dx,dy,dw,dh);
		}
	});
}

/// Mixin that implements reaction to touch events characteristic for a typical
/// button. Widgets implementing this mixin should be mounted as descendants to
/// a WtGuiHost (see guicore.kbtjsx).
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}var MxWtBasicButton = Kbt.Mixin.declare ([MxWidget],
{
	/// Set this button to be enabled or disabled
	setEnabled: Kbt.Func.placeholder, // (Boolean yes)

	/// Is invoked when button state (pressed, released, disabled) changes.
	/// Overrider is assumed to check the state via getButtonState if needed.
	onButtonStateChanged: Kbt.Func.virtual (function () {}), // ()

	/// Get current button state. There are 3 possible states: "pressed" (button
	/// is pushed down), "released" (button is released and can be pressed) and
	/// "disabled" (button is released and cannot be pressed).
	/// @treturn String Current button state
	getButtonState: Kbt.Func.placeholder, // ()

	/// Get event that will be posted to appEQ on clicking this button. Event
	/// type is "buttonClick".
	click: Kbt.Func.placeholder, // () => MxStaticEvent (eventType=="buttonClick")

	/// Trigger triggered on click
	triggerOnClick: Kbt.Func.placeholder, // () => Trigger

	/// Override to handle direct reaction to touch down. Assumed to service
	/// visual reaction only.
	onTouchDown: Kbt.Func.virtual (function () {}), // (Point)

	/// Override to handle direct reaction to touch move. Assumed to service
	/// visual reaction only.
	onTouchMove: Kbt.Func.virtual (function () {}), // (Point)

	/// Override to handle direct reaction to touch up. Assumed to service
	/// visual reaction only.
	onTouchUp: Kbt.Func.virtual (function () {}), // (Point)

	/// Override to handle direct reaction to touch reset. Assumed to service
	/// visual reaction only.
	onTouchReset: Kbt.Func.virtual (function () {}) // (Point)
},
  function ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var me = this,
		cursorAt = undefined,
		state = "released",
		enabled = true,
		clickEvent = this.markToDispose((makeStaticEvent ({ eventType: "buttonClick" }))),
		triggerOnClick = Kbt.newTrigger (),
		host = null;

	function setButtonState (newState)
	{
		if (newState!=state)
		{
			state = newState;
			me.onButtonStateChanged ();
		}
	}

	function releaseCaptureIfNeeded ()
	{
		if (host!=null && MxWidget.dynamicCast (me)==host.capturer ())
			host.dispatchMouseReset ();
	}

	var dispatcher = this.markToDispose((new function ()
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		var touchDown,touchUp,touchMove,touchReset;

		for (;;)
		{
			do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[touchDown] = [_Kbt$msg];_Kbt$consumed = true; if(touchDown.eventType=="touchDown"){;break;}else _Kbt$consumed = false;}} while (false);

			if (!enabled) continue;

			host = touchDown.host;
			cursorAt = touchDown.point;
			setButtonState ("pressed");
			me.onTouchDown (cursorAt);

			for (;;)
			{
				do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[touchMove,touchUp,touchReset] = [_Kbt$msg,_Kbt$msg,_Kbt$msg];_Kbt$consumed = true; if(touchMove.eventType=="touchMove")
					{[touchUp,touchReset] = [undefined,undefined];break;}else   if(touchUp.eventType=="touchUp")
					{[touchMove,touchReset] = [undefined,undefined];break;}else   if(touchReset.eventType=="touchReset"){[touchMove,touchUp] = [undefined,undefined];break;}else _Kbt$consumed = false;}} while (false);
				if (touchMove)
				{
					cursorAt = touchMove.point;
					me.onTouchMove (cursorAt);
					setButtonState (me.getRectLocal ().isInside (cursorAt)?
						"pressed" : "released");
					continue;
				}
				else if (touchReset)
				{
					releaseCaptureIfNeeded ();
					me.onTouchReset (cursorAt);
					setButtonState (enabled? "released" : "disabled");
				}
				else if (touchUp)
				{
					me.onTouchUp (cursorAt);
					setButtonState (enabled? "released" : "disabled");
					// post click event to appEQ
					if (enabled && me.getRectLocal ().isInside (cursorAt))
					{
						if (!clickEvent.postedIn ())
							appEQ.postStatic (clickEvent);
						doAsync ( kbtBind(this,function(){ triggerOnClick (); }));
					}
				}
				break;
			}
			cursorAt = undefined;
			state = "released";
		}
	}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}));

	this.click.implement (function ()
	{
		return clickEvent;
	});

	/// Trigger triggered on click
	this.triggerOnClick.implement (function ()
	{
		return triggerOnClick;
	});

	this.getButtonState.implement (function ()
	{
		return state;
	});

	this.setEnabled.implement (function (newEnabled)
	{
		if (enabled!=newEnabled)
		{
			enabled = newEnabled;
			releaseCaptureIfNeeded ();
			if (enabled) setButtonState ("released");
			else setButtonState ("disabled");
		}
	});

	var base = this.mxOverride (MxWidget,
	{
		onPointMessage: function (pointRel,message)
		{
			switch (message.eventType)
			{
			case "touchDown":
			case "touchUp":
			case "touchMove":
				if (enabled)
				{
					message.point = pointRel;
					dispatcher (message);
					return true;
				}
				else return false;
			case "touchReset":
				dispatcher (message);
				return true;
			}
			return base.onPointMessage (pointRel,message);
		},
		onParentChanged: function (newParent,prevParent)
		{
			dispatcher ({ eventType: "touchReset" });
			return base.onParentChanged (newParent,prevParent);
		}
	});

	// first destructor action
	this.markToDispose(kbtBind(this,function(){ releaseCaptureIfNeeded (); }
));}catch(e){this.markToDispose(function(){throw e;});this.dispose();}});

/// Mixin that implements a progress bar capable of displaying some given
/// progress. Provides a simple default rendering, which you can change by
/// overriding MxWidget::onDrawBackground.
var MxWtProgressBar = Kbt.Mixin.declare ([MxWidget],
{
	/// Set current value
	setCurrent: Kbt.Func.placeholder, // (Number newCurrent)
	/// Get current value
	getCurrent: Kbt.Func.placeholder, // () => Number
	/// Set maximum value
	setMax: Kbt.Func.placeholder, // (Number newMax)
	/// Get maximum value
	getMax: Kbt.Func.placeholder, // () => Number
},
  function ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var current = 0,max = 0;

	this.setCurrent.implement (function (newCurrent)
	{
		if (current!=newCurrent)
		{
			current = newCurrent;
			if (current<0) current = 0;
			if (current>max) current = max;
			this.invalidate ();
		}
	});

	this.getCurrent.implement (function () { return current; });

	this.setMax.implement (function (newMax)
	{
		Kbt.assert (newMax>=0,
			"MxWtProgressBar::setMax: must be non-negative");
		if (max!=newMax)
		{
			max = newMax;
			if (current>max) current = max;
			this.invalidate ();
		}
	});

	this.getMax.implement (function () { return max; });

	var base = this.mxOverride (MxWidget,
	{
		onDrawBackground: function (gc,leftTop)
		{
			base.onDrawBackground (gc,leftTop);

			var rect = Rect.fromPointAndSize (leftTop,this.getRect ().size ());
			if (rect.width<=0 || rect.height<=0) return;
			var widthToFill = max>0? current*rect.width/max : 0;
			gc.fillStyle = "#808080";
			gc.fillRect (leftTop.x,leftTop.y,rect.width,rect.height);
			gc.fillStyle = "#FFFFFF";
			gc.fillRect (leftTop.x,leftTop.y,widthToFill,rect.height);
			gc.strokeStyle = "#000000";
			gc.strokeRect (leftTop.x,leftTop.y,rect.width,rect.height);
		}
	});
}catch(e){this.markToDispose(function(){throw e;});this.dispose();}});


//
// tweeners.kbtjsx: coordinator.kbtjsx, indirect dependence on main
// Provides a toolset for tweening
//

// type:
// TweenData (conventional):
// {
// from: Number|Point,
// to: Number|Point,
// over: Number // milliseconds
// useFunction: // (Number(0-1)) => (Number), choose from members of TweenFunc
// // default is TweenFunc.linear
// easingMode: "easein"|"easeout"|"easeinout" (default is "easeout")
// onChanged: Function // (Number|Point), called when tween value has changed
// }

var TweenFunc =
{
	// simple linear tweening, use TweenFunc.linear
	linear: function (t) { return t; },

	// quadric tweening (f(t)=t^2), use TweenFunc.quadric
	quadric: function (t) { return t*t; },

	// cubic tweening (f(t)=t^3), use TweenFunc.cubic
	cubic: function (t) { return t*t*t; },

	// back-ease tweening (f(t)=t^3-t*amp*sin (PI*t)), use TweenFunc.back(amp)
	back: function (amp)
	{
		return function (t)
		{
			return t*t*t-t*amp*Math.sin (Math.PI*t);
		};
	},

	// circle tweening (f(t)=1-sqrt(1-t^2)), use TweenFunc.circle
	circle: function (t) { return 1.0-Math.sqrt (1-t*t); },

	// sine tweening (f(t)=1-sin(1-t)*PI/2), use TweenFunc.sine
	sine: function (t) { return 1.0-Math.sin(1-t)*Math.PI*0.5; }
};

/// Object that does tweening of a parameter over specific range and time and
/// notifies of its changes. Objects to tween can be Point or number.
/// Create by new Tween({TweenData}).
/// On completion, can either post a persistent event (owned by the Tweener) or
/// to call a user specified function. Disposing stops the tweening without
/// any notifications and withdraws completion event if one was posted.
  function Tweener (tweenData)
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	Kbt.assert (
		(!isNaN (+tweenData.from) && !isNaN (+tweenData.to))||
		(tweenData.from instanceof Point && tweenData.to instanceof Point),
		"Tweener: can only tween between two numbers or two points");
	var
		numeric = !isNaN (+tweenData.from),
		easingMode = tweenData.easingMode || "easeout",
		delta = numeric? (tweenData.to-tweenData.from)
			: tweenData.to.sub (tweenData.from),
		curTime = 0,
		maxTime = +tweenData.over,
		progress = 0,
		useFunction = tweenData.useFunction || TweenFunc.linear,
		onChanged = tweenData.onChanged || function () {},
		onCompleted = null,
		eventCompleted =
			this.markToDispose((makeAsyncSignal ({ eventType: "tweenCompleted" })));

	/// Returns signal (eventType=="tweenCompleted") that will be raised when
	/// the tweening is done
	/// @treturn MxAsyncSignal
	this.eventCompleted = function () { return eventCompleted; };

	/// Sets a function to call on completion. If set to not null, the function
	/// will be called instead of posting eventCompleted ().
	this.setOnCompleted = function (newOnCompleted)
	{
		onCompleted = newOnCompleted;
	};

	function calc (t)
	{
		var inOut = true;
		if (easingMode=="easein") inOut = false;
		if (easingMode=="easeinout")
		{
			inOut = t>0.5;
			t = inOut? t-0.5 : t;
		}
		var f = inOut? useFunction (t) : 1.0-useFunction (1.0-t);
		return numeric? tweenData.from+f*delta
			: tweenData.from.add (delta.mul (f));
	}

	var process = this.markToDispose((new function ()
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func, ["timerTick","tweenTick"])));

		// set initial value
		onChanged (tweenData.from);

		var tick = _Kbt$as.markToDispose((makeStaticEvent ({ eventType: "tweenTick" }))),
			frameTick;

		 do { frameTick = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(frameTick)(frameTick===appFrameTimer.tick ())
		),false,kbtBind (this,function(frameTick){
			// post tick on every frame tick (we shouldn't expect
			// appFrameTimer.tick () directly as it will be consumed and won't
			// be passed to other users)
			if (!tick.postedIn ()) appEQ.postStatic (tick);
		}));_Kbt$as.markToDispose(frameTick);} while (false);

		for (;;)
		{let _Kbt$as=new _Kbt$AutoScope();try
		{
			let nextTick;
			do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[nextTick] = [_Kbt$msg];_Kbt$consumed = true; if(nextTick===tick){;break;}else _Kbt$consumed = false;}} while (false);

			// TODO: consider pause
			curTime += appLastFrameElapsed;
			if (curTime>=maxTime) break;

			let value = calc (curTime/maxTime);
			onChanged (value);
		}

		// set final value and signal end of tweening
		catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}onChanged (tweenData.to);
		if (onCompleted) onCompleted ();
		else if (!eventCompleted.postedIn ())
			appEQ.postStaticPersistent (eventCompleted);
	}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}));
}

/// A static event that is posted when all pending operations related to it are
/// completed. Unlike more complex MxAsyncOpBarrier, which relies on expection
/// of given events from appEQ to indicate completion on operations,
/// SimpleBarrier offers operation completers to notify it by calling a given
/// callback function.
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}  function SimpleBarrier ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var me = this;
	this.eventType = "simpleBarrier";
	this.mxEnable (MxStaticEvent);

	var opsDone = 0,opsTotal = 0,opMark = {};

	/// Reset the barrier for new use
	this.reset = function ()
	{
		this.withdraw ();
		opsDone = opsTotal = 0;
		opMark = {};
	};

	function post ()
	{
		appEQ.postStaticPersistent (me);
		opMark = null;
	}

	/// Return total number of operations on the barrier
	/// @treturn Number Number of operations to trigger the barrier
	this.opsTotal = function () { return opsTotal; };

	/// Return number of operations that are completed
	/// @treturn Number Number of operations that already triggered the barrier
	this.opsDone = function () { return opsDone; };

	/// Adds new operation to wait on this barrier. Returns a function that
	/// can be called
	/// @treturn MxDisposable+Function A MxDisposable and a function. Call it
	/// as a function with no args to signal
	this.newOperation = function ()
	{
		Kbt.assert (opsTotal==0 || opsDone<opsTotal,
			"SimpleBarrier: already completed, use reset before re-use");

		var triggered = false,curOpMark = opMark;
		function signaler ()
		{
			Kbt.assert (curOpMark===opMark,
				"SimpleBarrier: stale signaler invocation");
			if (!triggered)
			{
				triggered = true;
				opsDone++;
				if (opsDone>=opsTotal) post ();
			}
		}
		Kbt.Mixin.enable (signaler);
		signaler.mxEnable (MxDisposable);
		signaler.markToDispose(kbtBind(this,function(){
			if (!triggered && opMark===curOpMark)
			{
				triggered = true;
				opsTotal--;
				if (opsDone>=opsTotal) post ();
			}
		}
		));opsTotal++;
		return signaler;
	};
}
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}


//
// screenman.kbtjsx: guicore.kbtjsx, coordinator.kbtjsx
// Provides ScreenManager namespace and class for a basic screen (MxScreen)
//

var ScreenManager =
{
	_$currentScreen: null, // MxScreen (interface)

	/// @treturn MxScreen (root) Screen currently activated or being activated
	currentScreen: function ()
	{
		return this._$currentScreen? this._$currentScreen.mxRoot () : null;
	}
};

var MxScreen = Kbt.Mixin.declare ([MxDisposable],
{
	/// Internal. Issues a forced deactivation command to the screen, usually
	/// when another screen is commanded to beginActivate while previous one
	/// has not explicitly blocked itself in $waitForActivation. In this case,
	/// previous screen is implicitly enters $waitForActivation from its current
	/// blocking point.
	internal$forceDeactivate: Kbt.Func.placeholder, // crt ()

	/// Protected, coroutine blocker. Blocks the calling coroutine until screen
	/// receives command from another coroutine via beginActivate. Use:
	/// #[this.$waitForActivation] () (from screen's coroutine). If this screnn
	/// is currently activated, raises this.eventDeactivated () signal before
	/// going blocked. Returns with reference to
	/// Your screen is assumed to invoke it at the start after it has set up
	/// its basics in order to wait for first activation, and later every time
	/// when it goes deactivated and yields to some other screen. After this
	/// function returns, your screen is assumed to play transition animation
	/// (if any) and invoke this.$confirmActivationCompleted.
	/// ONLY CALL FROM THIS SCREEN'S COROUTINE!
	/// @treturn MxScreen (interface) Screen that was previously activated. Your
	/// screen coroutine may use it to draw the screen moving away in playing
	/// transition sequence.
	$waitForActivation: Kbt.Func.placeholder, // crt () => MxScreen

	/// Protected. Posts activation signal (this.eventActivated ()) to appEQ.
	/// ONLY CALL FROM THIS SCREEN'S COROUTINE!
	$confirmActivationCompleted: Kbt.Func.placeholder, // ()

	/// Protected, coroutine blocker, pure virtual. Screen's main function.
	$main: Kbt.Func.virtual, // crt ()

	/// Protected. Launches screen coroutine. Call only once, from your screen's
	/// constructor, AFTER you have overridden $main.
	/// DO NOT CALL FROM SCREEN'S OWN COROUTINE!
	$start: Kbt.Func.placeholder,

	/// Issues an activation command to the screen. Screen is assumed to perform
	/// transition sequence in its coroutine and to raise this.eventActivated
	/// after it is done. Unraises screen's deactivation signal.
	/// DO NOT CALL FROM SCREEN'S OWN COROUTINE!
	beginActivate: Kbt.Func.placeholder, // ()

	/// To override. Draw screen on a given GC. Assumed to render screen at GC,
	/// where top left corner is located at (0,0). Has default implementation
	/// (renders this.guiHost ()). Can be used both for drawing screen as is
	/// and for drawing it deformed/faded for transition purposes (a screen
	/// currently fading in can render previous screen above or under itself).
	draw: Kbt.Func.virtual (Kbt.Func.placeholder), // (GC gc)

	/// Get screen's GUI host (for either dispatching a message or drawing)
	/// @treturn WtGuiHost GUI host of this screen
	guiHost: Kbt.Func.placeholder, // () => WtGuiHost

	/// Returns async signal that is posted when the screen is activated (i.
	/// e., when $confirmActivationCompleted is called in screen's coroutine).
	/// @treturn MxAsyncSignal Event, eventType=="screenActivated"
	eventActivated: Kbt.Func.placeholder, // () => MxAsyncSignal, eventType=="screenDeactivated"

	/// Returns async signal that is posted when the screen is deactivated (i.
	/// e., when $waitForActivation is called in screen's coroutine).
	/// @treturn MxStaticEvent Event, eventType=="screenDeactivated"
	eventDeactivated: Kbt.Func.placeholder, // () => MxStaticEvent, eventType=="screenDeactivated"

	/// Returns true if the screen is completely activated (i. e., its coroutine
	/// has called $confirmActivationCompleted).
	isActivated: Kbt.Func.placeholder // () => Boolean
},
  function ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var
		me = this,
		guiHost = Kbt.multiApply (this.markToDispose((new WtGuiHost ())),
		{
			setRect: new Rect (0,0, 480 , 640 )
		}),
		eventActivated =
			this.markToDispose((makeAsyncSignal ({ eventType: "screenActivated" }))),
		eventDeactivated =
			this.markToDispose((makeAsyncSignal ({ eventType: "screenDeactivated" }))),
		eventBeginActivate =
			this.markToDispose((makeStaticEvent ({ eventType: "screenBeginActivate" }))),
		eventForceDeactivate =
			this.markToDispose((makeStaticEvent ({ eventType: "screenForceDeactivate" }))),
		activated = false;

	this.guiHost.implement (function () { return guiHost; });
	this.waitingForReactivation = false;

	var main;

	this.internal$forceDeactivate.implement (function ()
	{
		if (activated)
		{
			eventForceDeactivate.withdraw ();
			appEQ.postStaticPersistent (eventForceDeactivate);
		}
	});

	this.$waitForActivation.implement (function ()
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		if (ScreenManager._$currentScreen==me)
		{
			eventBeginActivate.withdraw ();
			eventDeactivated.withdraw ();
			appEQ.postStaticPersistent (eventDeactivated);
			activated = false;
			guiHost.dispatchMouseReset ();
		}

		// wait for activation command
		var act,prevScreen;
		me.waitingForReactivation = true;
		do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[act] = [_Kbt$msg];_Kbt$consumed = true; if(act===eventBeginActivate){;break;}else _Kbt$consumed = false;}} while (false);
		me.waitingForReactivation = false;
		// set new current screen
		prevScreen = ScreenManager._$currentScreen;
		ScreenManager._$currentScreen = me;
		do {_Kbt$result = (prevScreen);return;} while (false) ;
	}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;});

	this.$confirmActivationCompleted.implement (function ()
	{
		eventActivated.withdraw ();
		appEQ.postStaticPersistent (eventActivated);
		activated = true;
	});

	this.$start.implement (function ()
	{
		Kbt.assert (!main,
			"MxScreen::$start: screen coroutine already launched");
		main = me.markToDispose((new function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
			var deact;
			 do { {let _Kbt$innerIt=function(deact){if(false)yield undefined;{let _Kbt$as=new _Kbt$AutoScope();try{
				// forced deactivation interruptor
				if (activated)
					do {let func=new(me.$waitForActivation )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}};deact=new _Kbt$Observer(_Kbt$observers,kbtBind(this,function(deact)(deact===eventForceDeactivate)
			),true,function(){return new _Kbt$innerIt(_Kbt$msg);});_Kbt$as.markToDispose (deact);}} while (false);
			do {let func=new(me.$main )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
		}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}));
	});

	this.beginActivate.implement (function ()
	{
		if (ScreenManager._$currentScreen && ScreenManager._$currentScreen!=me)
			ScreenManager._$currentScreen.internal$forceDeactivate ();
		if (ScreenManager._$currentScreen!=me ||
			ScreenManager._$currentScreen.waitingForReactivation)
		{
			eventDeactivated.withdraw ();
			eventForceDeactivate.withdraw ();
			appEQ.postStaticPersistent (eventBeginActivate);
		}
		else
		{
			// already activated, just raise the confirmation to comply with the
			// convention
			appLog.writef ("Already activated<br>\n");
			me.$confirmActivationCompleted ();
		}
	});

	this.draw.implement (function (gc)
	{
		guiHost.draw (gc);
	});

	this.eventActivated.implement (function () { return eventActivated; });
	this.eventDeactivated.implement (function () { return eventDeactivated; });

	this.isActivated.implement (function () { return activated; });

	this.markToDispose(kbtBind(this,function(){
		// if dying while still active, handle it gracefully
		if (ScreenManager._$currentScreen===me)
		{
			ScreenManager._$currentScreen = null;
			guiHost.dispatchMouseReset ();
		}
	}
));}catch(e){this.markToDispose(function(){throw e;});this.dispose();}});


//
// popupman.kbtjsx: guicore.kbtjsx, coordinator.kbtjsx
// Provides PopupManager namespace and class for a basic modal popup (MxPopup)
//

var PopupManager =
{
	_$popups: new Kbt.List, // MxPopup (interface)

	/// Number, 0 to 1. If currentPopup is not null, indicates phase of popup's
	/// fade in/fade out sequence. UI dispatcher may use it to draw the popup
	/// in a particular phase. When 1, popup is completely shown and can be
	/// fed with input events.
	activationPhase: 0,

	/// @treturn Number Total number of popups currently activated. 0 if there
	/// are no activated popups.
	totalPopups: function ()
	{
		return this._$popups.count ();
	},

	/// @treturn MxPopup (root) Topmost of currently activated popups. If null,
	/// no popups are activated, and the input events should be dispatched to
	/// current screen.
	currentPopup: function ()
	{
		return this._$popups.count ()?
			this._$popups.lastNode ().value.mxRoot () : null;
	}
};

var MxPopup = Kbt.Mixin.declare ([MxDisposable],
{
	/// Protected, coroutine blocker, pure virtual. Popup's main function.
	/// May receive messages { eventType: "activationChanged", activated: Boolean }
	/// as part of reaction to activate/deactivate commands, is sent when the
	/// popup is activated/deactivated respectively.
	$main: Kbt.Func.virtual, // crt ()

	/// Protected. Launches screen coroutine. Call only once, from your screen's
	/// constructor, AFTER you have overridden $main.
	/// DO NOT CALL FROM POPUP'S OWN COROUTINE!
	$start: Kbt.Func.placeholder,

	/// Coroutine blocker. Activates the popup, returns when it's activated.
	/// Coroutine must accept eventType=="tweenCompleted" if it uses event type
	/// filters.
	/// DO NOT CALL FROM POPUP'S OWN COROUTINE!
	activate: Kbt.Func.placeholder, // ()

	/// Coroutine blocker. Deactivates the popup, returns when it's deactivated.
	/// Coroutine must accept eventType=="popupDeactivated" if it uses event
	/// type filters.
	deactivate: Kbt.Func.placeholder, // ()

	/// Draw popup on a given GC. Renders popup's guiHost at GC, where top left
	/// corner is located at (0,0). To be used by popups/screen dispatcher and
	/// renderer.
	draw: Kbt.Func.placeholder, // (GC gc)

	/// Get popup's GUI host, mainly for dispatching a message
	/// @treturn WtGuiHost GUI host of this screen
	guiHost: Kbt.Func.placeholder, // () => WtGuiHost

	/// Returns true if the popup is activated (but not necessarily topmost).
	isActivated: Kbt.Func.placeholder // () => Boolean
},
  function ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var
		me = this,
		manLink = new Kbt.ListNode (this),
		guiHost = Kbt.multiApply (this.markToDispose((new WtGuiHost ())),
		{
			setRect: new Rect (0,0, 480 , 640 )
		});

	this.guiHost.implement (function () { return guiHost; });

	var main;

	this.activate.implement (function ()
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		if (me.isActivated ()) return; // already activated

		// add to top of stack
		PopupManager._$popups.addLast (manLink);
		var tweenIn = _Kbt$as.markToDispose((new Tweener (
		{
			// use plain linear tween, it's up to dispatcher to do rendering
			// twists
			from: 0,
			to: 1,
			over:  300 ,
			onChanged:  kbtBind(this,function(d) { PopupManager.activationPhase = d; }
		)})));
		var tweenInDone;
		do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[tweenInDone] = [_Kbt$msg];_Kbt$consumed = true; if(tweenInDone===tweenIn.eventCompleted ()){;break;}else _Kbt$consumed = false;}} while (false);
		main ({eventType: "activationChanged", activated: true});
		// ...dats essentially all
	}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;});

	this.deactivate.implement (function ()
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		if (!me.isActivated ()) return; // already deactivated

		if (PopupManager._$popups.lastNode ().value===me)
		{
			// if me is at top, show tween out
			var tweenOut = _Kbt$as.markToDispose((new Tweener (
			{
				// use plain linear tween, it's up to dispatcher to do rendering
				// twists
				from: 1,
				to: 0,
				over:  300 ,
				onChanged:  kbtBind(this,function(d) { PopupManager.activationPhase = d; }
			)})));
			var tweenOutDone;
			do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[tweenOutDone] = [_Kbt$msg];_Kbt$consumed = true; if(tweenOutDone===tweenOut.eventCompleted ()){;break;}else _Kbt$consumed = false;}} while (false);
			PopupManager._$popups.remove (manLink);
		}
		else
			// not current - just silently remove
			PopupManager._$popups.remove (manLink);

		main ({eventType: "activationChanged", activated: false});
	}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;});

	this.$start.implement (function ()
	{
		Kbt.assert (!main,
			"MxScreen::$start: screen coroutine already launched");
		main = me.markToDispose((new function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
			do {let func=new(me.$main )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
		}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}));
	});

	this.draw.implement (function (gc)
	{
		guiHost.draw (gc);
	});

	this.isActivated.implement (function () { return manLink.list ()!=null; });

	this.markToDispose(kbtBind(this,function(){
		// if dying while still active, handle it gracefully
		if (manLink.list ()===PopupManager._$popups)
		{
			var meCurrent = (PopupManager._$popups.lastNode ().value===this);
			PopupManager._$popups.remove (manLink);
			guiHost.dispatchMouseReset ();
			if (meCurrent)
			{
				// if me was current, take care of proper activation phase for
				// underlying popup (if any)
				PopupManager.activationPhase =
					(PopupManager._$popups.count ()<=0)? 0 : 1;
			}
		}
	}
));}catch(e){this.markToDispose(function(){throw e;});this.dispose();}});


//
// sprite.kbtjsx: graphutil.kbtjsx, coordinator.kbtjsx
// Provides classes for animated sprites, Sprite and SpriteLayer
//

// types:
// Subimage (conventional): { image: ImageData, srcRect: Rect, size: Size }
// SpriteFrame (conventional):
// {
//  picture: Subimage,
//  refOffset: Point // offset from top left of picture to reference point,
//  // in percentage of picture size
//  // 0,0 - top left of picture, 1,1 - bottom right
//  // sprite's setRefPoint will specify layer position of exactly this point
//  // from the picture
// }
// SpriteFrameTick (conventional):
// { eventType: "spriteFrameTick", deltaTime: Number }
// SpriteLayerChanged (conventional):
// { eventType: "spriteLayerChanged", layer: SpriteLayer }

var spritesList = new Kbt.List (); // of Sprite

  function Sprite ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var refPoint = new Point (0,0),
		mods = new Kbt.List (),
		alpha = 1,
		frame = null,
		spriteLink = new Kbt.ListNode (this),
		layerLink = new Kbt.ListNode (this),
		animator = null,
		inAnimator = false;

	this.markToDispose(kbtBind(this,function(){
		if (spriteLink.list ()) spritesList.remove (spriteLink);
		if (layerLink.list ()) layerLink.list ().remove (layerLink);
		do  { if (animator) { (animator).dispose (); animator= null; } } while (0) ;
	}

	));this.friend$layerLink = layerLink;

	// for invocation by SpriteLayer
	this.friend$render = function (gc)
	{
		if (frame)
		{let _Kbt$as=new _Kbt$AutoScope();try
		{
			_Kbt$as.markToDispose((saveGCState (gc)));
			var
				picture = frame.picture,
				offX = -frame.refOffset.x*picture.size.width,
				offY = -frame.refOffset.y*picture.size.height,
				modOffsetX = 0,
				modOffsetY = 0,
				modRotation = 0,
				modScaleX = 1.0,
				modScaleY = 1.0,
				modAlpha = 1.0;
			// setup transforms & alpha
			for (var mod in mods.elements ())
			{
				modOffsetX += mod.offset.x;
				modOffsetY += mod.offset.y;
				modScaleX *= mod.scale.x;
				modScaleY *= mod.scale.y;
				modRotation += mod.rotation;
				modAlpha *= mod.alpha;
			}
			if (modScaleX<=0 && modScaleY<=0) return;
			gc.translate (refPoint.x+modOffsetX,refPoint.y+modOffsetY);
			gc.rotate (modRotation);
			gc.scale (modScaleX,modScaleY);
			// and, finally, draw it!!
			gc.globalAlpha = gc.globalAlpha*alpha*modAlpha;
			gc.drawImage (picture.image,
				picture.srcRect.x,picture.srcRect.y,
				picture.srcRect.width,picture.srcRect.height,
				offX,offY,
				picture.size.width,picture.size.height);
		}
	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}};

	/// Set sprite frame
	/// @tparam SpriteFrame newFrame New frame (or null if no frame)
	this.setFrame = function (newFrame)
	{ frame = newFrame; };

	/// Set coords of sprite's reference point (relative to its sprite layer)
	/// @tparam Point newRefPoint Reference point, relative to sprite layer
	this.setRefPoint = function (newRefPoint)
	{ refPoint = newRefPoint; };

	/// Get sprite's current reference point
	/// @treturn Point Reference point, relative to sprite layer
	this.getRefPoint = function ()
	{ return refPoint; };

	/// Adds a set of modifiers - a MxDisposable of following structure:
	/// {
	///  scale: Point, // scale
	///  rotation: Number, // rotation around ref point
	///  offset: Point, // offset from ref point (unaffected by scale and rotation)
	///  alpha: Number // additional alpha multiplier
	/// }
	/// All modifiers to the sprite are applied in addition to its refPoint
	/// offset. Their set is such that order in which they are applied is not
	/// important. If sprite is controlled by several controllers, each of them
	/// should use its own mod.
	/// @trerutn MxDisposable Set of modifiers that is also a disposable - you
	/// can dispose it to remove this set from sprite's modifiers list.
	this.addMods = function ()
	{
		return new   function ()
		{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
			var link = new Kbt.ListNode (this);
			mods.addLast (link);
			this.markToDispose(kbtBind(this,function(){ mods.remove (link); }
			));this.scale = new Point (1.0,1.0);
			this.offset = new Point (0,0);
			this.rotation = 0;
			this.alpha = 1.0;
		}catch(e){this.markToDispose(function(){throw e;});this.dispose();}};
	};

	/// Set sprite alpha
	/// @tparam Number newAlpha New alpha (0...1)
	this.setAlpha = function (newAlpha)
	{ alpha = newAlpha; };

	/// Get sprite master alpha
	/// @treturn Number Alpha (0...1)
	this.getAlpha = function ()
	{ return alpha; };

	/// Set animator coroutine for this sprite. The coroutine has access to the
	/// sprite and is able to change its various parameters. It also receives
	/// messages with eventType=="spriteFrameTick" (see SpriteFrameTick) per
	/// each sprite's progressFrameTick, eventType=="spriteLayerChanged" per
	/// every external (i. e., not from animator) change of sprite layer and can
	/// receive any custom user message sent via sendMsgToAnimator.
	/// @tparam Crt(Sprite sprite) newAnimator Coroutine initializer that must
	/// take one parameter, which will be set to this sprite and may be used by
	/// the coroutine change sprite parameters. May be null which means no
	/// animator coroutine.
	this.setAnimator = function (newAnimator)
	{
		do  { if (animator) { (animator).dispose (); animator= null; } } while (0) ;
		animator = newAnimator? new newAnimator (this) : null;
	};

	/// Send an arbitrary message to current animator coroutine. Return the
	/// coroutine reply if any, or undefined if message was not accepted.
	this.sendMsgToAnimator = function (msg)
	{
		if (animator)
		{let _Kbt$as=new _Kbt$AutoScope();try
		{
			inAnimator = true;
			_Kbt$as.markToDispose(kbtBind(this,function(){ inAnimator = false; }
			));animator (msg);
			return animator.reply;
		}
	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}};

	/// Set sprite's layer (null if no layer). If not null, the sprite will be
	/// placed to its end. Position in the list affects order of rendering and
	/// enumeration, use moveToLayerBefore/moveToLayerAfter if you need more
	/// precise positioning before or after particular other sprites.
	/// Sprite can only be at one layer at a time. Setting new layer
	/// automatically removes the sprite from its previous one.
	/// @tparam SpriteLayer spriteLayer New sprite layer (or null if no layer)
	this.setLayer = function (spriteLayer)
	{
		if (layerLink.list ()) layerLink.list ().remove (layerLink);
		if (spriteLayer) spriteLayer.friend$list.addLast (layerLink);
		if (!inAnimator)
			this.sendMsgToAnimator ({
				eventType: "spriteLayerChanged",
				layer: spriteLayer
			});
	};

	/// Get sprite's current layer. The layer is null by default.
	/// @treturn SpriteLayer Sprite's current layer
	this.getLayer = function ()
	{
		return layerLink.list ()? layerLink.list ()._$SpriteLayer : null;
	};

	/// Place the sprite on same layer as beforeSprite currently is, and before
	/// it.
	/// @tparam Sprite beforeSprite Sprite to place this one before
	this.moveToLayerBefore = function (beforeSprite)
	{
		Kbt.assert (beforeSprite,
			"Sprite::moveToLayerBefore: reference sprite must be not null");
		if (layerLink.list ()) layerLink.list ().remove (layerLink);
		var newLayer = beforeSprite.getLayer ();
		if (newLayer)
			newLayer.friend$list.addBefore
				(beforeSprite.friend$layerLink,layerLink);
	};

	/// Place the sprite on same layer as beforeSprite currently is, and after
	/// it.
	/// @tparam Sprite afterSprite Sprite to place this one after
	this.moveToLayerAfter = function (beforeSprite)
	{
		Kbt.assert (beforeSprite,
			"Sprite::moveToLayerBefore: reference sprite must be not null");
		if (layerLink.list ()) layerLink.list ().remove (layerLink);
		var newLayer = beforeSprite.getLayer ();
		if (newLayer)
			newLayer.friend$list.addAfter
				(beforeSprite.friend$layerLink,layerLink);
	};

	/// Process frame tick by updating animation, if any
	/// @tparam ms Number Number of milliseconds to progress
	this.processFrameTick = function (ms)
	{
		this.sendMsgToAnimator ({
			eventType: "spriteFrameTick",
			deltaTime: ms
		});
	};
}

/// Helper intended for use in an animator coroutine. Create in animator and
/// use waitFrames method to wait for a given number of frames.
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}Sprite.FrameWaiter = new function ()
{
	var msConsumed = 0;

	this.waitFrames = function (frameCount)
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		var spriteFrameTick;
		for (var i=0; i<frameCount; i++)
		{
			if (msConsumed>33.333333333333336 )
			{
				msConsumed -= 33.333333333333336 ;
				continue;
			}
			else
			{
				do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[spriteFrameTick] = [_Kbt$msg];_Kbt$consumed = true;
					if(spriteFrameTick.eventType=="spriteFrameTick"){;break;}else _Kbt$consumed = false;}} while (false);
				msConsumed += spriteFrameTick.deltaTime;
			}
		}
	}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;};
};

/// Sprite layer. Its only purpose is to group sprites for drawing.
/// If you want to clip a certain set of sprites against a particular region,
/// or to render a certain set of sprites always over another set, or want
/// to render some of them to a bitmap cache, you just need to create a separate
/// SpriteLayer for this group and add your sprites of interest to it.
/// Enables MxDisposable and ImxEnumerable.
  function SpriteLayer ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var sprites = new Kbt.List ();
	sprites._$SpriteLayer = this;

	this.markToDispose(kbtBind(this,function(){
		sprites.clear ();
	}

	));this.friend$list = sprites;

	/// Draw layer's sprites on a given gc.
	/// @tparam GC HMTL 2d canvas graphics context
	this.render = function (gc)
	{
		for (var sprite in sprites.elements ())
			sprite.friend$render (gc);
	};

	this.count = function ()
	{
		return sprites.count ();
	};

	// SpriteLayer is ImxEnumberable
	this.mxEnable (ImxEnumerable);
	this.mxOverride (ImxEnumerable,
	{
		elements: function () { return sprites.elements (); }
	});
}
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}


var images =
{
	BUTT_GO: function() beginLoadImage ("img/butt_go.png"),

	BG_POPUP: function() beginLoadImage ("img/popup_bg.png"),
	BUTTS_POPUP: function() beginLoadImage ("img/butts_popup.png"),

	KBTLOGO: function() beginLoadImage ("img/kbtlogo.png"),
	BG_TITLE: function() beginLoadImage ("img/bg_title.png"),
	BUTTS_MAINMENU: function() beginLoadImage ("img/butts_mainmenu.png"),	

	HELP_1: function() beginLoadImage ("img/help1.png"),
	HELP_2: function() beginLoadImage ("img/help2.png"),
	HELP_3: function() beginLoadImage ("img/help3.png"),

	BG_GAME: function() beginLoadImage ("img/bg_game.jpg"),
	PONIES_COLORED: function() beginLoadImage ("img/ponies_colored.png"),
	PONY_CHROMA: function() beginLoadImage ("img/pony_chroma.png"),
	BOOT: function() beginLoadImage ("img/boot.png"),
	GAME_BOARD: function() beginLoadImage ("img/game_board.jpg"),
	FX_SPLATS: function() beginLoadImage ("img/fx_splats.png"),
	FX_ZAP: function() beginLoadImage ("img/fx_zap.png"),
	FX_EXPLODE: function() beginLoadImage ("img/fx_explode.png"),
	FX_SPECULAR: function() beginLoadImage ("img/fx_specular.png"),
	CROSSHAIR: function() beginLoadImage ("img/crosshair.png")};

var imageClips;

function initImageClips ()
{
	function singleImage (imageData)
	{
		var result =
		{
			image: imageData,
			srcRect: new Rect (0,0,imageData.width,imageData.height),
			size: new Size (imageData.width,imageData.height)
		};
		return result; // doesn't want to return directly
	}

	function subImageGrid (imageData,cols,rows)
	{
		var width = imageData.width/cols,height = imageData.height/rows;
		return Kbt.patchObject (function (col,row)
		{
			var result =
			{
				image: imageData,
				srcRect: new Rect (width*col,height*row,width,height),
				size: new Size (width,height)
			};
			return result;
		},
		{
			size: new Size (width,height)
		});
	}

	imageClips =
	{
		KBTLOGO: singleImage (images.KBTLOGO),
		BG_TITLE: singleImage (images.BG_TITLE),
		BG_POPUP: subImageGrid (images.BG_POPUP,1,3),
		BUTT_GO: subImageGrid (images.BUTT_GO,1,2),
		BUTTS_MAINMENU: subImageGrid (images.BUTTS_MAINMENU,1,6),
		BUTTS_POPUP: subImageGrid (images.BUTTS_POPUP,1,6),

		HELP_1: singleImage (images.HELP_1),
		HELP_2: singleImage (images.HELP_2),
		HELP_3: singleImage (images.HELP_3),

		BG_GAME: singleImage (images.BG_GAME),
		PONIES_COLORED: subImageGrid (images.PONIES_COLORED,7,7),
		PONY_CHROMA: singleImage (images.PONY_CHROMA),
		BOOT: singleImage (images.BOOT),
		GAME_BOARD: singleImage (images.GAME_BOARD),
		FX_SPLATS: subImageGrid (images.FX_SPLATS,7,1),
		FX_ZAP: subImageGrid (images.FX_ZAP,7,1),
		FX_EXPLODE: subImageGrid (images.FX_EXPLODE,4,1),
		FX_SPECULAR: singleImage (images.FX_SPECULAR),
		CROSSHAIR: subImageGrid (images.CROSSHAIR,2,1)
	};
}


var sprites,animations;

function initSpritesAndAnimations ()
{
	sprites =
	{
		coloredPony: function (color,frame)
		{
			var result = {
				picture: imageClips.PONIES_COLORED (frame,color),
				refOffset:  new Point (0.5,0.5) };
			return result;
		},
		chromaFace: {
			picture: imageClips.PONY_CHROMA,
			refOffset:  new Point (0.5,0.5) },
		specular:
		{
			picture: imageClips.FX_SPECULAR,
			refOffset:  new Point (0.5,0.5) },
		boot:
		{
			picture: imageClips.BOOT,
			refOffset: new Point (0.5,0)
		},
		crosshair: function (on)
		{
			var result = {
				picture: imageClips.CROSSHAIR (on? 1 : 0,0),
				refOffset:  new Point (0.5,0.5) };
			return result;
		},
		splat: function (color)
		{
			var result = {
				picture: imageClips.FX_SPLATS (color,0),
				refOffset:  new Point (0.5,0.5) };
			return result;
		},
		zap: function (color)
		{
			var result = {
				picture: imageClips.FX_ZAP (color,0),
				refOffset: new Point (0.5,0)
			};
			return result;
		},
		explode: function (phase)
		{
			var result = {
				picture: imageClips.FX_EXPLODE (phase,0),
				refOffset:  new Point (0.5,0.5) };
			return result;
		}
	};

	// animations
	animations =
	{
		genPony: function (color)
		{
			return function (sprite)
			{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
				Kbt.multiApply (sprite,
				{
					setFrame: sprites.coloredPony (color, 0 )
				});

				var setStand,setFall,setKnockout;
				
					do { setStand = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(setStand)(setStand.eventType=="setStand")
					),false,kbtBind (this,function(setStand){
						sprite.setFrame
							(sprites.coloredPony (color, 0 ));
					} ));_Kbt$as.markToDispose(setStand);
					 setFall = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(setFall)(setFall.eventType=="setFall")
					),false,kbtBind (this,function(setFall){
						sprite.setFrame
							(sprites.coloredPony (color, 1 ));
					} ));_Kbt$as.markToDispose(setFall);
					 setKnockout = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(setKnockout)(setKnockout.eventType=="setKnockout")
					),false,kbtBind (this,function(setKnockout){
						sprite.setFrame
							(sprites.coloredPony (color, 2 ));
					}));_Kbt$as.markToDispose(setKnockout);} while (false);

				do {let func=new(appEQ.endlessLoop )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;};
		},

		specular: function (sprite)
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			Kbt.multiApply (sprite,
			{
				setFrame: sprites.specular,
				setAlpha: 0.75
			});

			var angle = 0,
				pulsePhase = 0,
				momentum = Math.random ()<0.5? -6.283185307179586 : 6.283185307179586 ,
				pulseMaxRate = 6.283185307179586 ,
				tick,
				mod = sprite.addMods ();
			 do { tick = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(tick)(tick.eventType=="spriteFrameTick")
			),false,kbtBind (this,function(tick){
				// specular rotation
				angle += momentum*tick.deltaTime*0.001;
				if (angle<0) angle += 6.283185307179586 ;
				if (angle>6.283185307179586 ) angle -= 6.283185307179586 ;
				// specular pulsing
				pulsePhase += pulseMaxRate
					*Math.sin (Math.random ()*6.283185307179586 )
					*tick.deltaTime*0.001;
				if (pulsePhase<0) pulsePhase += 6.283185307179586 ;
				if (pulsePhase>6.283185307179586 ) pulsePhase -= 6.283185307179586 ;
				mod.rotation = angle;
				var pulseAmp = (Math.sin (pulsePhase)+1)*0.5*0.5+0.5;
				mod.scale = new Point (pulseAmp,pulseAmp);
			}));_Kbt$as.markToDispose(tick);} while (false);

			do {let func=new(appEQ.endlessLoop )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
		}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;},

		powerPony: function (color)
		{
			return function (sprite)
			{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
				Kbt.multiApply (sprite,
				{
					setFrame: sprites.coloredPony (color, 3 )
				});

				// auxiliary specular on teeth
				var specular = Kbt.multiApply (_Kbt$as.markToDispose((new Sprite ())),
				{
					moveToLayerAfter: sprite,
					setAnimator: animations.specular,
					setRefPoint: sprite.getRefPoint ()
						.add (Layouts.screenGame.specularOffset)
				})

				var setStand,setFall,tick,layerChanged;
				
					do { setStand = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(setStand)(setStand.eventType=="setStand")
					),false,kbtBind (this,function(setStand){
						sprite.setFrame
							(sprites.coloredPony (color, 3 ));
					} ));_Kbt$as.markToDispose(setStand);
					 setFall = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(setFall)(setFall.eventType=="setFall")
					),false,kbtBind (this,function(setFall){
						sprite.setFrame
							(sprites.coloredPony (color, 4 ));
					} ));_Kbt$as.markToDispose(setFall);
					 tick = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(tick)(tick.eventType=="spriteFrameTick")
					),false,kbtBind (this,function(tick){
						// drag tooth specular along
						specular.setRefPoint (sprite.getRefPoint ()
							.add (Layouts.screenGame.specularOffset));
					} ));_Kbt$as.markToDispose(tick);
					 layerChanged = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(layerChanged)(layerChanged.eventType=="spriteLayerChanged")
					),false,kbtBind (this,function(layerChanged){
						specular.moveToLayerAfter (sprite);
					}));_Kbt$as.markToDispose(layerChanged);} while (false);

				do {let func=new(appEQ.endlessLoop )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;};
		},

		shockPony: function (color)
		{
			return function (sprite)
			{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
				Kbt.multiApply (sprite,
				{
					setFrame: sprites.coloredPony (color, 5 )
				});

				var setStand,setFall,tick,
					mod = sprite.addMods ();
				
					do { setStand = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(setStand)(setStand.eventType=="setStand")
					),false,kbtBind (this,function(setStand){
						sprite.setFrame
							(sprites.coloredPony (color, 5 ));
					} ));_Kbt$as.markToDispose(setStand);
					 setFall = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(setFall)(setFall.eventType=="setFall")
					),false,kbtBind (this,function(setFall){
						sprite.setFrame
							(sprites.coloredPony (color, 6 ));
					} ));_Kbt$as.markToDispose(setFall);
					 tick = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(tick)(tick.eventType=="spriteFrameTick")
					),false,kbtBind (this,function(tick){
						// vibrate randomly
						mod.offset =
							new Point (Math.random ()*1-0.5,Math.random ()*1-0.5);
					}));_Kbt$as.markToDispose(tick);} while (false);

				do {let func=new(appEQ.endlessLoop )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;};
		},

		chromaFace: function (sprite)
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			Kbt.multiApply (sprite,
			{
				setFrame: sprites.chromaFace
			});

			var setStand,setFall,
				mod = sprite.addMods (),
				bouncePhase = 0,
				momentum = 6.283185307179586 ;
			
				do { tick = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(tick)(tick.eventType=="spriteFrameTick")
				),false,kbtBind (this,function(tick){
					// bounce
					bouncePhase += momentum*tick.deltaTime*0.001;
					if (bouncePhase<0) bouncePhase += 6.283185307179586 ;
					if (bouncePhase>6.283185307179586 )
						bouncePhase -= 6.283185307179586 ;
					mod.offset =
						new Point (0,Math.sin (bouncePhase)*2);
				}));_Kbt$as.markToDispose(tick);} while (false);

			do {let func=new(appEQ.endlessLoop )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
		}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;},

		splat: function (color)
		{
			return function (sprite)
			{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
				_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
				Kbt.multiApply (sprite,
				{
					setFrame: sprites.splat (color)
				});

				var mod = _Kbt$as.markToDispose((sprite.addMods ()));

				_Kbt$as.markToDispose((new Tweener (
				{
					from: 0.25,
					to: 1,
					over: 250,
					useFunction: TweenFunc.back (1),
					easingMode: "easein",
					onChanged:  kbtBind(this,function(d)
					{ mod.scale = new Point (d,d); }
				)})));

				do {let func=new(appEQ.endlessLoop )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;};
		},

		zap: function (color)
		{
			return function (sprite)
			{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
				_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
				var zap1 = Kbt.multiApply (_Kbt$as.markToDispose((new Sprite ())),
					{
						moveToLayerAfter: sprite,
						setFrame: sprites.zap (color)
					}),
					zapMod1 = Kbt.patchObject (zap1.addMods (),
					{
						rotation: 0
					}),
					zap2 = Kbt.multiApply (_Kbt$as.markToDispose((new Sprite ())),
					{
						moveToLayerAfter: sprite,
						setFrame: sprites.zap (color)
					}),
					zapMod2 = Kbt.patchObject (zap2.addMods (),
					{
						rotation: 1.5707963267948966 }),
					zap3 = Kbt.multiApply (_Kbt$as.markToDispose((new Sprite ())),
					{
						moveToLayerAfter: sprite,
						setFrame: sprites.zap (color)
					}),
					zapMod3 = Kbt.patchObject (zap3.addMods (),
					{
						rotation: 3.141592653589793 }),
					zap4 = Kbt.multiApply (_Kbt$as.markToDispose((new Sprite ())),
					{
						moveToLayerAfter: sprite,
						setFrame: sprites.zap (color)
					}),
					zapMod4 = Kbt.patchObject (zap4.addMods (),
					{
						rotation: 4.71238898038469 });
				for each (zap in [zap1,zap2,zap3,zap4])
					Kbt.multiApply (zap,
					{
						setFrame: sprites.zap (color),
						setRefPoint: sprite.getRefPoint ()
					});

				var tick,layerChanged;
				var phase = 1.0;
				
					do { tick = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(tick)(tick.eventType=="spriteFrameTick")
					),false,kbtBind (this,function(tick){
						if (phase>0)
						phase -= tick.deltaTime*0.002 ;
						for each (zapMod in [zapMod1,zapMod2,zapMod3,zapMod4])
							zapMod.scale = new Point (phase,1.0);
						for each (zap in [zap1,zap2,zap3,zap4])
							zap.setRefPoint (sprite.getRefPoint ());
					} ));_Kbt$as.markToDispose(tick);
					 layerChanged = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(layerChanged)(layerChanged.eventType=="spriteLayerChanged")
					),false,kbtBind (this,function(layerChanged){
						zap1.moveToLayerAfter (sprite);
						zap2.moveToLayerAfter (sprite);
						zap3.moveToLayerAfter (sprite);
						zap4.moveToLayerAfter (sprite);
					}));_Kbt$as.markToDispose(layerChanged);} while (false);

				do {let func=new(appEQ.endlessLoop )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;};
		},

		explode: function (sprite)
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));

			var mod = _Kbt$as.markToDispose((sprite.addMods ()));
			_Kbt$as.markToDispose((new function ()
			{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
				_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
				var tweenDone;
				var tweenIn = _Kbt$as.markToDispose((new Tweener (
				{
					from: 0,
					to: 1,
					over: 250,
					useFunction: TweenFunc.quadric,
					easingMode: "easein",
					onChanged:  kbtBind(this,function(d)
					{ mod.scale = new Point (d,d); }
				)})));
				do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[tweenDone] = [_Kbt$msg];_Kbt$consumed = true; if(tweenDone==tweenIn.eventCompleted ()){;break;}else _Kbt$consumed = false;}} while (false);
				var tweenOut = _Kbt$as.markToDispose((new Tweener (
				{
					from: 1,
					to: 0,
					over: 250,
					useFunction: TweenFunc.quadric,
					easingMode: "easeout",
					onChanged:  kbtBind(this,function(d)
					{ mod.scale = new Point (d,d); }
				)})));
				do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[tweenDone] = [_Kbt$msg];_Kbt$consumed = true; if(tweenDone==tweenOut.eventCompleted ()){;break;}else _Kbt$consumed = false;}} while (false);
			} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}()));

			for (var i=0; i<3; i++)
			{
				sprite.setFrame (sprites.explode (i));
				do {let func=new(appEQ.sleep )(125),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			}

			do {let func=new(appEQ.endlessLoop )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
		}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;},
	};
}



//
// morewidgets.kbtjsx: basicwidgets.kbtjsx
// Some advanced widgets (WtImageClipButton)
//

// type:
// Subimage (conventional): { image: ImageData, srcRect: Rect, size: Size }

/// Implements a button that is rendered via single image clip.
  function WtImageClipButton ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var
		imagePressed = null,
		imageReleased = null,
		imageDisabled = null;

	this.mxEnable (MxWtBasicButton);
	var imageToDisplay = Kbt.multiApply (this.markToDispose((new WtImageClip ())),
		{
			setParent: this,
			setRect: this.getRectLocal (),
			setAligns: { h: "center", v: "middle" }
		});

	var base = this.mxOverride (MxWtBasicButton,
	{
		onRectChanged: function (newRect,prevRect)
		{
			if (!newRect.size ().equal (prevRect.size ()))
				imageToDisplay.setRect (Rect.fromPointAndSize
					(new Point (0,0),newRect.size ()));
			base.onRectChanged (newRect,prevRect);
		},
		onButtonStateChanged: function ()
		{
			switch (this.getButtonState ())
			{
			case "pressed":
				imageToDisplay.setImageClip (imagePressed); break;
			case "released":
				imageToDisplay.setImageClip (imageReleased); break;
			case "disabled":
				imageToDisplay.setImageClip (imageDisabled); break;
			}
		}
	});

	/// Set image clip to display for pressed state
	/// @tparam Subimage newImagePressed
	this.setPressedPicture = function (newImagePressed)
	{
		imagePressed = newImagePressed;
		if (this.getButtonState ()=="pressed")
			imageToDisplay.setImageClip (imagePressed);
	};

	/// Set image clip to display for released state
	/// @tparam Subimage newImageReleased
	this.setReleasedPicture = function (newImageReleased)
	{
		imageReleased = newImageReleased;
		if (this.getButtonState ()=="released")
			imageToDisplay.setImageClip (imageReleased);
	};

	/// Set image clip to display for disabled state
	/// @tparam Subimage newImageDisabled
	this.setDisabledPicture = function (newImageDisabled)
	{
		imageDisabled = newImageDisabled;
		if (this.getButtonState ()=="disabled")
			imageToDisplay.setImageClip (imageDisabled);
	};
}
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}


//
// popupgen.kbtjsx: popupman.kbtjsx, morewidgets.kbtjsx, resources.kbtjsx,
//  const.kbtjsx
// Provides class for a generic HvH modal popup (MxHvhPopup) - border, one (OK)
// or two (YES+NO) buttons and a number of user-defined components stacked and
// aligned to user's selection. Particular popups are derived from MxHvhPopup.
// Also provides classes for popup components (MxHvhPopupComponent (base, abstract),
// HvhPcText, HvhPcPicture, HvhPcMargin).
//

var MxHvhPopup = Kbt.Mixin.declare ([MxPopup],
{
	/// Coroutine blocker. Shows the popup and blocks until user presses button.
	/// Returns true if user pressed "yes" or "ok" or false if he pressed "no".
	/// @tparam mode String "ok" or "yesno"
	/// @treturn Boolean true if user pressed "yes"/"ok", false if "no"
	show: Kbt.Func.placeholder, // (mode) => Boolean

	/// Protected. Add a component to the popup with a given alignment. The
	/// component will be placed at bottom of previously added component.
	/// @tparam align String "left", "right" or "center", horizontal alignment
	/// for the component.
	$addComponent: Kbt.Func.placeholder // (MxHvhPopupComponent,hzAlign)
},
  function ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var
		me = this,
		host = Kbt.multiApply (this.markToDispose((new MxWidget ())),
		{
			setParent: this.guiHost (),
			enableBitmapCache: true
		}),
		// top of popup background (fixed)
		picTop = Kbt.multiApply (this.markToDispose((new WtImageClip ())),
		{
			setParent: host,
			setImageClip: imageClips.BG_POPUP (0,0),
			setStretchMode: "fill"
		}),
		// middle of popup background (resizeable)
		picMiddle = Kbt.multiApply (this.markToDispose((new WtImageClip ())),
		{
			setParent: host,
			setImageClip: imageClips.BG_POPUP (0,1),
			setStretchMode: "fill"
		}),
		// bottom of popup background (fixed)
		picBottom = Kbt.multiApply (this.markToDispose((new WtImageClip ())),
		{
			setParent: host,
			setImageClip: imageClips.BG_POPUP (0,2),
			setStretchMode: "fill"
		}),
		// button of one-button mode
		buttOk = Kbt.multiApply (this.markToDispose((new WtImageClipButton ())),
		{
			setParent: host,
			setPressedPicture: imageClips.BUTTS_POPUP (0,1),
			setReleasedPicture: imageClips.BUTTS_POPUP (0,0)
		}),
		// 'yes' button of two-button mode
		buttYes = Kbt.multiApply (this.markToDispose((new WtImageClipButton ())),
		{
			setParent: host,
			setPressedPicture: imageClips.BUTTS_POPUP (0,3),
			setReleasedPicture: imageClips.BUTTS_POPUP (0,2)
		}),
		// 'no' button of two-button mode
		buttNo = Kbt.multiApply (this.markToDispose((new WtImageClipButton ())),
		{
			setParent: host,
			setPressedPicture: imageClips.BUTTS_POPUP (0,5),
			setReleasedPicture: imageClips.BUTTS_POPUP (0,4)
		});
	var comps = []; // { comp: MxHvhPopupComponent, align: "left"/"right"/"center" }

	this.markToDispose(kbtBind(this,function(){ disposeArray (comps); }

	));this.mxOverride (MxPopup,
	{
		$main: function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			// nothing essential is going on here
			var blockMe;
			do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[blockMe] = [_Kbt$msg];_Kbt$consumed = true; if(false){;break;}else _Kbt$consumed = false;}} while (false);
		}
	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}});

	this.$addComponent.implement (function (component,align)
	{
		comps.push ({ comp: component.retain (), align: align });
	});

	this.show.implement (function (mode)
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		var positions = [], // of Rect
			y = Layouts.popup.topMarginHeight;
		for each (var compAndAlign in comps)
		{
			var comp = compAndAlign.comp,
				size = comp.friend$measure ();
			switch (compAndAlign.align)
			{
			case "left":
			default:
				x = Layouts.popup.widthMargin; break;
			case "right":
				x = Layouts.popup.width-Layouts.popup.widthMargin-size.width;
				break;
			case "center":
				x = (Layouts.popup.width-size.width)*0.5;
				break;
			}
			positions.push (new Rect (x,y,size.width,size.height));
			y += size.height;
		}
		// correct height if it turns out less than summary height of borders
		if (y+Layouts.popup.bottomMarginHeight<
			Layouts.popup.topBorderHeight+
			Layouts.popup.bottomBorderHeight)
			y = Layouts.popup.topBorderHeight+
				Layouts.popup.bottomBorderHeight-
				Layouts.popup.bottomMarginHeight;

		// arrange things
		for (var i in comps)
			comps[i].comp.friend$place (host,positions[i]);

		// place buttons
		var totalPopupHeight = y+Layouts.popup.bottomMarginHeight,
			buttTopY = (Layouts.popup.bottomMarginHeight-
				Layouts.popup.buttonHeight)*0.5+
				totalPopupHeight-Layouts.popup.bottomMarginHeight,
			centerX = Layouts.popup.width*0.5;
		switch (mode)
		{
		case "ok":
		default:
			buttYes.setParent (null);
			buttNo.setParent (null);
			Kbt.multiApply (buttOk,
			{
				setParent: host,
				setRect: new Rect (centerX-Layouts.popup.buttonWidth*0.5,
					buttTopY,
					Layouts.popup.buttonWidth,
					Layouts.popup.buttonHeight)
			});
			break;
		case "yesno":
			buttOk.setParent (null);
			Kbt.multiApply (buttYes,
			{
				setParent: host,
				setRect: new Rect (centerX-Layouts.popup.buttonWidth-
						Layouts.popup.twoButtonMargin*0.5,
					buttTopY,
					Layouts.popup.buttonWidth,
					Layouts.popup.buttonHeight)
			});
			Kbt.multiApply (buttNo,
			{
				setParent: host,
				setRect: new Rect (centerX+Layouts.popup.twoButtonMargin*0.5,
					buttTopY,
					Layouts.popup.buttonWidth,
					Layouts.popup.buttonHeight)
			});
			break;
		}

		// locate background components and the container itself
		host.setRect (new Rect (( 480 -Layouts.popup.width)*0.5,
			( 640 -totalPopupHeight)*0.5,
			Layouts.popup.width,totalPopupHeight));
		picTop.setRect (new Rect (0,0,
			Layouts.popup.width,Layouts.popup.topBorderHeight));
		picBottom.setRect (
			new Rect (0,totalPopupHeight-Layouts.popup.bottomBorderHeight,
			Layouts.popup.width,Layouts.popup.bottomBorderHeight));
		picMiddle.setRect (
			new Rect (0,Layouts.popup.topBorderHeight,
			Layouts.popup.width,
			totalPopupHeight-Layouts.popup.bottomBorderHeight
			-Layouts.popup.topBorderHeight));

		do {let func=new(me.activate )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);

		// expect action from buttons
		var answer,click;
		do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[click] = [_Kbt$msg];_Kbt$consumed = true; if( (click===buttYes.click () && (answer="yes")) ||
			(click===buttNo.click () && (answer="no")) ||
			(click===buttOk.click () && (answer="ok")) ){;break;}else _Kbt$consumed = false;}} while (false);

		do {let func=new(me.deactivate )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
		do {_Kbt$result = (answer!="no");return;} while (false) ;
	}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;});

	this.$start ();
}catch(e){this.markToDispose(function(){throw e;});this.dispose();}});

var MxHvhPopupComponent = Kbt.Mixin.declare ([MxDisposable],
{
	/// Friend to MxHvhPopup. Measure dimensions of a popup component
	friend$measure: Kbt.Func.virtual, // () => Size

	/// Friend to MxHvhPopup. Place component under a given parent to a given
	/// parent-local point.
	friend$place: Kbt.Func.virtual // (MxWidget,Point)
});

  function HvhPcText ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var label = Kbt.multiApply (this.markToDispose((new WtLabel ())),
		{
			setAutoLines: 0,
			setColor: "#000000",
			setRect: new Rect (0,0,Layouts.popup.textWidth,0),
			setAligns: { h: "center", v: "top" }
		});

	this.setFontDesc = function (fontDesc)
	{ label.setFontDesc (fontDesc); };

	this.setText = function (text)
	{ label.setText (text); };

	this.setColor = function (color)
	{ label.setColor (color); };

	this.setHorizAlign = function (halign)
	{ label.setAligns ({ h: halign, v: "top" }); };

	this.mxEnable (MxHvhPopupComponent);
	this.mxOverride (MxHvhPopupComponent,
	{
		friend$measure: function ()
		{
			var size = label.actualTextSize ();
			return new Size (Layouts.popup.textWidth,size.height);
		},
		friend$place: function (newParent,at)
		{
			Kbt.multiApply (label,
			{
				setParent: newParent,
				setRect: Rect.fromPointAndSize (at,this.friend$measure ())
			});
		}
	});
}

catch(e){this.markToDispose(function(){throw e;});this.dispose();}}  function HvhPcPicture ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var imageWt = Kbt.multiApply (this.markToDispose((new WtImageClip ())),
		{
			setStretchMode: "none"
		}),
		picture = null;

	this.setPicture = function (subimage)
	{
		picture = subimage;
		imageWt.setImageClip (subimage);
	};

	this.mxEnable (MxHvhPopupComponent);
	this.mxOverride (MxHvhPopupComponent,
	{
		friend$measure: function ()
		{
			return picture? picture.size : new Size (0,0);
		},
		friend$place: function (newParent,at)
		{
			Kbt.multiApply (imageWt,
			{
				setParent: newParent,
				setRect: Rect.fromPointAndSize (at,this.friend$measure ())
			});
		}
	});
}

catch(e){this.markToDispose(function(){throw e;});this.dispose();}}  function HvhPcMargin ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	this.mxEnable (MxHvhPopupComponent);
	this.mxOverride (MxHvhPopupComponent,
	{
		friend$measure: function ()
		{
			return new Size (0,Layouts.popup.marginHeight);
		},
		friend$place: function (newParent,at)
		{
			// don't need to do anything here
		}
	});
}
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}


//
// popups.kbtjsx: popupgen.kbtjsx
// A set of application specific popups
//

//
// Asking popup with a title and text
//
  function HvhPopupQuery (title,text)
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	this.mxEnable (MxHvhPopup);

	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 18, bold: true },
		setHorizAlign: "center",
		setColor: "#800080",
		setText: title
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 14 },
		setHorizAlign: "center",
		setColor: "#000000",
		setText: text
	}),
	"center");
}


//
// "About" popup
//
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}  function HvhPopupAbout ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	this.mxEnable (MxHvhPopup);

	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 24, bold: true },
		setHorizAlign: "center",
		setColor: "#000000",
		setText: "HITLAR"
	}),
	"center");

	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 12, bold: true },
		setHorizAlign: "center",
		setColor: "#808080",
		setText: "VS"
	}),
	"center");

	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 18, bold: true },
		setHorizAlign: "center",
		setColor: "#800080",
		setText: "HORSEMEAT"
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 14 },
		setHorizAlign: "center",
		setColor: "#000000",
		setText:
"Kbt Framework demo application.\r\n\
Precompiled version. See source.html and *.kbtjsx for source"
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));

	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcPicture ())),
	{
		setPicture: imageClips.KBTLOGO
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 12 },
			setHorizAlign: "center",
			setColor: "#000000",
			setText: Kbt.format (
"Kbt Framework ver %KBVER% (c) 2011 Subterranean Devil\r\n\
is an experimental JavaScript extension facility\r\n\
",
				{
					"KBVER": Kbt.version,
					"APPVER":  "1.0" }),
	}),
	"center");
}

//
// "How to play" popup
//
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}  function HvhPopupHowToPlay ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	this.mxEnable (MxHvhPopup);

	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 18, bold: true },
		setHorizAlign: "center",
		setColor: "#800080",
		setText: "HOW TO PLAY"
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 14 },
		setHorizAlign: "center",
		setColor: "#000000",
		setText:
"Drag clip to select current piece of pony scrap."
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcPicture ())),
	{
		setPicture: imageClips.HELP_1
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 14 },
		setHorizAlign: "center",
		setColor: "#000000",
		setText:
"Click on a bioreactor cell to paint pony in it with color of the current pony scrap."
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcPicture ())),
	{
		setPicture: imageClips.HELP_2
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 14 },
		setHorizAlign: "center",
		setColor: "#000000",
		setText:
"Three or more ponies of matching color in a row or a column are disposed and free up space for new ones. The longer the match is, the better."
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcPicture ())),
	{
		setPicture: imageClips.HELP_3
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 14 },
		setHorizAlign: "center",
		setColor: "#000000",
		setText:
"Your pony scrap supply is limited. Ponies you dispose are recycled into new scrap at a certain rate."
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 14 },
		setHorizAlign: "center",
		setColor: "#000000",
		setText:
"Ponies disposed via chain reactions are recycled with greater efficiency."
	}),
	"center");
}

//
// "Power pony" popup
//
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}  function HvhPopupPowerPony ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	this.mxEnable (MxHvhPopup);

	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 18, bold: true },
		setHorizAlign: "center",
		setColor: "#800080",
		setText: "GRENADE STUFFED PONY"
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcPicture ())),
	{
		setPicture: imageClips.PONIES_COLORED ( 3 ,0)
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 14 },
		setHorizAlign: "center",
		setColor: "#000000",
		setText:
"Match of 4 ponies delivers pony stuffed with a grenade. When painted or otherwisely touched, its explosion disposes 8 ponies from 8 adjacent cells."
	}),
	"center");
}

//
// "Shock pony" popup
//
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}  function HvhPopupShockPony ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	this.mxEnable (MxHvhPopup);

	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 18, bold: true },
		setHorizAlign: "center",
		setColor: "#800080",
		setText: "ELECTROCUTED PONY"
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcPicture ())),
	{
		setPicture: imageClips.PONIES_COLORED ( 5 ,2)
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 14 },
		setHorizAlign: "center",
		setColor: "#000000",
		setText:
"Linear or crossing match of 5 ponies delivers an electrocuted pony. When painted or otherwisely touched, it releases a zap that disposes all ponies in its row and column."
	}),
	"center");
}

//
// "Chroma face" popup
//
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}  function HvhPopupChromaFace ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	this.mxEnable (MxHvhPopup);

	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 18, bold: true },
		setHorizAlign: "center",
		setColor: "#800080",
		setText: "PONYFAG"
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcPicture ())),
	{
		setPicture: imageClips.PONY_CHROMA
	}),
	"center");

	this.$addComponent (this.markToDispose((new HvhPcMargin ())));
	this.$addComponent (Kbt.multiApply (this.markToDispose((new HvhPcText ())),
	{
		setFontDesc: { height: 14 },
		setHorizAlign: "center",
		setColor: "#000000",
		setText:
"Linear or crossing match of 6 and more ponies delivers a ponyfag. It can't be matched, but can be disposed by painting, zap or explosion. When painted, it disposes all ponies of the hitting color."
	}),
	"center");
}
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}


//
// scr_mainmenu.kbtjsx: screenman.kbtjsx, morewidgets.kbtjsx, resources.kbtjsx,
//  tweeners.kbtjsx
// Main menu screen (ScreenMainMenu)
//

var howToPlayShown = false;
  function ScreenMainMenu ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	this.response = null;

	this.mxEnable (MxScreen);
	var
		me = this,
		myGuiHost = this.guiHost (),
		// create background image
		background = Kbt.multiApply (this.markToDispose((new WtImageClip ())),
		{
			setParent: myGuiHost,
			setRect: new Rect (0,0, 480 , 640 ),
			setImageClip: imageClips.BG_TITLE
		}),
		buttPlay = Kbt.multiApply (me.markToDispose((new WtImageClipButton ())),
		{
			setParent: myGuiHost,
			setRect: Layouts.screenMainMenu.buttPlay,
			setPressedPicture: imageClips.BUTTS_MAINMENU (0,1),
			setReleasedPicture: imageClips.BUTTS_MAINMENU (0,0)
		}),
		buttDontPlay = Kbt.multiApply (me.markToDispose((new WtImageClipButton ())),
		{
			setParent: myGuiHost,
			setRect: Layouts.screenMainMenu.buttDontPlay,
			setPressedPicture: imageClips.BUTTS_MAINMENU (0,3),
			setReleasedPicture: imageClips.BUTTS_MAINMENU (0,2)
		}),
		buttAbout = Kbt.multiApply (me.markToDispose((new WtImageClipButton ())),
		{
			setParent: myGuiHost,
			setRect: Layouts.screenMainMenu.buttAbout,
			setPressedPicture: imageClips.BUTTS_MAINMENU (0,5),
			setReleasedPicture: imageClips.BUTTS_MAINMENU (0,4)
		});
	
	var prevScreenToRender,prevScreenPhase;
	var base = this.mxOverride (MxScreen,
	{
		draw: function (gc)
		{
			// it is quite like default, but just for reminder how this
			// proc should look in simplest case
			myGuiHost.draw (gc);
			base.draw (gc);

			if (prevScreenToRender)
			{let _Kbt$as=new _Kbt$AutoScope();try
			{
				_Kbt$as.markToDispose((saveGCState (gc)));

				// remember, transformations are applied in reverse way!
				gc.translate ( 480 *0.5, 640 *0.5);
				if (prevScreenPhase>0) // FF bug - scale (0,0) screws the canvas
					gc.scale (prevScreenPhase,prevScreenPhase);
				gc.translate (- 480 *0.5,- 640 *0.5);

				gc.globalAlpha = prevScreenPhase;
				if (prevScreenPhase>0) prevScreenToRender.draw (gc);
			}
		catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}},
		$main: function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			do {let func=new(me.$waitForActivation )() ,prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl; var prevScreen =func.result;} while (false);
			{let _Kbt$as=new _Kbt$AutoScope();try
			{
				prevScreenToRender = prevScreen;
				// cache fading-out screen for performance
				prevScreen.guiHost ().enableBitmapCache (true);
				let transIn = _Kbt$as.markToDispose((new Tweener (
				{
					from: 1,
					to: 0,
					over:  300 ,
					useFunction: TweenFunc.back (0.25),
					onChanged:  kbtBind(this,function(d) { prevScreenPhase = d; }
				)})));

				let transInDone;
				do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[transInDone] = [_Kbt$msg];_Kbt$consumed = true; if(transInDone==transIn.eventCompleted ()){;break;}else _Kbt$consumed = false;}} while (false);
				prevScreenToRender = null;
			}
			catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}me.$confirmActivationCompleted ();

			for (;;)
			{let _Kbt$as=new _Kbt$AutoScope();try
			{
				let clickPlay,clickDontPlay,clickAbout;
				do {
					for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[clickPlay,clickDontPlay,clickAbout] = [_Kbt$msg,_Kbt$msg,_Kbt$msg];_Kbt$consumed = true; if(clickPlay===buttPlay.click ()) {[clickDontPlay,clickAbout] = [undefined,undefined];break;}else 
					 if(clickDontPlay===buttDontPlay.click ()) {[clickPlay,clickAbout] = [undefined,undefined];break;}else 
					 if(clickAbout===buttAbout.click ()){[clickPlay,clickDontPlay] = [undefined,undefined];break;}else _Kbt$consumed = false;}} while (false);
				if (clickAbout)
				{let _Kbt$as=new _Kbt$AutoScope();try
				{
					do {let func=new(_Kbt$as.markToDispose((new HvhPopupAbout ())).show )("ok"),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
				}
				catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}else if (clickPlay)
				{
					if (!howToPlayShown)
					{let _Kbt$as=new _Kbt$AutoScope();try
					{
						howToPlayShown = true;
						do {let func=new(_Kbt$as.markToDispose((new HvhPopupHowToPlay ())).show )("ok"),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
					}
					catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}me.response = "newGame";
					do {let func=new(me.$waitForActivation )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false); // return response to dispatcher and sleep
					me.$confirmActivationCompleted (); // dispatcher invoked us again
					me.response = null;
				}
				else if (clickDontPlay)
				{let _Kbt$as=new _Kbt$AutoScope();try
				{
					do {let func=new(_Kbt$as.markToDispose((new HvhPopupQuery
						("DON'T PLAY","You are not playing, what's the problem?"))
						).show )("ok"),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
						/*
						.show] ("yesno")
						=> (var yesno);
					if (yesno)
					{
						me.response = "endGame";
						#[me.$waitForActivation] (); // return response to dispatcher and sleep
						me.$confirmActivationCompleted (); // dispatcher invoked us again
						me.response = null;
					}
					*/
				}
			catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}
		catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}
	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}});

	this.mxFreeze ();
	this.$start ();
}
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}


  function Game ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	var me = this;
	var board = [];
	this.board = board; // expose to outside
	this.matchesCount = 0; // count of disjoint matches currently found in game

	for (var x=0; x< 8 ; x++)
	{
		let col = [];
		board.push (col);
		for (var y=0; y< 8 ; y++)
			col.push ({
				ponyColor:  -1 ,
				ponyType:  0 ,
				lockType:  0 ,
				matchId: -1,
				matchWhileFallout: false,
				directChange: null,
				matchChange: null,
				toCreate: null,
				causedBy: null,
				updateId: 0
			});
	}

	// mark the board with matches and lock ponies for corresponding removal
	var newMatched = [];
	this.detectMatches = function ()
	{
		newMatched.length = 0;

		function isCellMatchable (cell)
		{
			return cell.ponyType!= 0 &&
				(cell.lockType== 0 ||
				cell.lockType== 2 );
		}

		for (var y=0; y< 8 ; y++)
		for (var x=0; x< 8 ; x++)
		{
			// reset the attempt
			var cell = board[x][y];
			cell.matchId = -1;
			cell.xSpan = 1; // width of horiz single color span this pony is in
			cell.ySpan = 1; // width of vert single color span this pony is in
		}

		for (var y=0; y< 8 ; y++)
		for (var x=0; x< 8 ; x++)
		{
			// cell to trace from
			var cell = board[x][y];
			// empty or unmatchable cell, skip it
			if (!isCellMatchable (cell)) continue;
			// trace spans from this cell and increment span counts for
			// other cells of the span:
			// rightwards
			for (var i=x+1; i< 8 ; i++)
			{
				var cell1 = board[i][y];
				if (!isCellMatchable (cell1) ||
					cell1.ponyColor!=cell.ponyColor)
					break; // end of span
				cell1.xSpan++;
			}
			// leftwards
			for (var i=x-1; i>=0; i--)
			{
				var cell1 = board[i][y];
				if (!isCellMatchable (cell1) ||
					cell1.ponyColor!=cell.ponyColor)
					break; // end of span
				cell1.xSpan++;
			}
			// downwards
			for (var j=y+1; j< 8 ; j++)
			{
				var cell1 = board[x][j];
				if (!isCellMatchable (cell1) ||
					cell1.ponyColor!=cell.ponyColor)
					break; // end of span
				cell1.ySpan++;
			}
			// upwards
			for (var j=y-1; j>=0; j--)
			{
				var cell1 = board[x][j];
				if (!isCellMatchable (cell1) ||
					cell1.ponyColor!=cell.ponyColor)
					break; // end of span
				cell1.ySpan++;
			}
		}

		// markup matches
		this.matchesCount = 0;
		function markMatch (xFrom,yFrom,matchId,color)
		{
			if (xFrom<0 || xFrom>= 8 ||
				yFrom<0 || yFrom>= 8 ) return; // out of board
			var cell = board[xFrom][yFrom];
			if ((cell.xSpan<3 && cell.ySpan<3)||
				cell.matchId!=-1 ||
				cell.ponyColor!=color)
				return; // not in a match or already marked
			cell.matchId = matchId;
			newMatched.push ({ x: xFrom, y: yFrom });
			markMatch (xFrom-1,yFrom,matchId,color);
			markMatch (xFrom+1,yFrom,matchId,color);
			markMatch (xFrom,yFrom-1,matchId,color);
			markMatch (xFrom,yFrom+1,matchId,color);
		}
		for (var y=0; y< 8 ; y++)
		for (var x=0; x< 8 ; x++)
		{
			var cell = board[x][y];
			// found an unmarked cell of a match - mark the whole match with
			// a new mark
			if ((cell.xSpan>=3 || cell.ySpan>=3) && cell.matchId==-1)
				markMatch (x,y,this.matchesCount++,cell.ponyColor);
		}
	};

	// in:
	// common fields - type: string (name). All below labeled by type:
	// "idle": {}
	// "move": { changes: [{ x: int, y: int, ponyColor: int, ponyType: int }] }
	// out:
	// common fields - type: string (name), done: bool. All below labeled by
	// type:
	// "visMarkMatch": { x: int, y: int }
	// "visRemove": { x: int, y: int, causeType: "normal"|"explode"|"shock"|"chroma"|"create",
	//  ponyColor: int,
	//  causeX: int, causeY: int } // last 2 for explode, shock or chroma that caused the removal
	// "visRemoveExplode": { x: int, y: int, causeColor: int, causeType: ..., collateral: [{ x: int, y: int }] }
	// "visRemoveShock": { x: int, y: int, ponyColor: int, causeColor: int, causeType: ..., collateral: [{ x: int, y: int }] }
	// "visRemoveChroma": { x: int, y: int, ponyColor: int, causeColor: int, causeType: ..., collateral: [{ x: int, y: int }] }
	// "visCreatePower": { x: int, y: int, ponyColor: int, src: [{ x: int, y: int }] }
	// "visCreateShock": { x: int, y: int, ponyColor: int, src: [{ x: int, y: int }] }
	// "visCreateChroma": { x: int, y: int, src: [{ x: int, y: int }] }
	// "visFallout": { x: int, y: int, xfrom: int, yfrom: int(m/b<0) }
	// "visMoveOk": { changes: [{ x: int, y: int, prevColor: int, newColor: int,
	//  prevType: int, newType: int }] } // the only reply element if present
	// "visMoveReject": {} // the only reply element if present, no "done"
	// "visResetCascade": {} // the only reply element if present, also signal for state to go stable
	// "visNewCascade": { cascadeCount: int, poniesRemoved: int }
	var mainLoopOut = [];
	function mainLoop (newGame)
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		var fallout = false,
			postFallout = false,
			latestUpdateId = 0,
			currentVisualizations = new Kbt.List (),
			cascadeCount = 0;
		MAIN: for (;;)
		{
			var inIdle,inMove;
			do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[inIdle,inMove] = [_Kbt$msg,_Kbt$msg];_Kbt$consumed = true; if(inIdle.type=="idle") {[inMove] = [undefined];break;}else  // time tick
				 if(inMove.type=="move"){[inIdle] = [undefined];break;}else _Kbt$consumed = false;}} while (false); // player made a move
			mainLoopOut.length = 0;
			var newRemovals = 0;
			if (inMove)
			{
				// check if move is acceptable
				for each (change in inMove.changes)
				if (change.x<0 || change.y<0 ||
					change.x>= 8 ||
					change.y>= 8 ||
					board[change.x][change.y].ponyType== 0 ||
					board[change.x][change.y].lockType!= 0 )
				{
					// can't change out-of-board, empty or locked cells
					mainLoopOut = [{ type: "visMoveReject" }];
					continue MAIN;
				}
				// (TODO: add check for formation of matches if turn
				// into classical match-3)
				// ...

				// changes accepted - check for direct blasts (TODO: don't if
				// turn into classical match-3), apply changes and fill request
				// to visualize changes
				var changesToReply = [];
				for each (change in inMove.changes)
				{
					var cell = board[change.x][change.y];
					switch (cell.ponyType)
					{
					case  2 :
						cell.directChange = { type: "explode",
							causeType: "normal",
							ponyColor: cell.ponyColor };
						break;
					case  3 :
						cell.directChange = { type: "shock",
							causeType: "normal",
							ponyColor: cell.ponyColor };
						break;
					case  4 :
						cell.directChange = { type: "chroma",
							causeType: "normal",
							// note, color taken from change in this case!
							ponyColor: change.ponyColor };
						break;
					}
					changesToReply.push ({ x: change.x, y: change.y,
						prevType: cell.ponyType, prevColor: cell.ponyColor,
						newType: change.ponyType, newColor: change.ponyColor });
					cell.ponyColor = change.ponyColor;
					cell.ponyType = change.ponyType;
					cell.updateId = ++latestUpdateId;
				}
				mainLoopOut.push ({ type: "visMoveOk", done: false,
					changes: changesToReply });
				continue MAIN;
			}
			else if (inIdle)
			{
				me.detectMatches ();
				// will fill
				if (!fallout)
				{
					// check for existing matches
					var existingMatches = {}; // matchId => [{x: int, y: int}]
					for (var x=0; x< 8 ; x++)
					for (var y=0; y< 8 ; y++)
					{
						var cell = board[x][y];
						if (cell.matchId!=-1)
						{
							if (!existingMatches[cell.matchId])
								existingMatches[cell.matchId] = { cells: [] };
							var matchData = existingMatches[cell.matchId];
                            matchData.cells.push ({ x: x, y: y });

                            // also, track the most recently updated cell in the
                            // match
                            if (!matchData.recent ||
                            	board[matchData.recent.x][matchData.recent.y]
                            	.updateId<cell.updateId)
                            	matchData.recent = { x: x, y: y };
						}
					}

					// mark ponies of each match to remove in appropriate manner
					for each (match in existingMatches)
					{
						// calculate type and color of advanced pony to create
						// (if any)
						var newAdPonyType =  0 ,
							newAdPonyColor;
						if (match.cells.length==4)
						{
							newAdPonyType =  2 ;
							newAdPonyColor =
								board[match.recent.x][match.recent.y].ponyColor;
						}
						else if (match.cells.length==5)
						{
							newAdPonyType =  3 ;
							newAdPonyColor =
								board[match.recent.x][match.recent.y].ponyColor;
						}
						else if (match.cells.length>=6)
						{
							newAdPonyType =  4 ;
							newAdPonyColor =  -1 ;
						}
						// mark cells of this match for appropriate types of
						// removal and advanced pony creation
						var toCreateSrc = [];
						for each (cellCrd in match.cells)
						{
							var cell = board[cellCrd.x][cellCrd.y];
							switch (cell.ponyType)
							{
							case  1 :
								cell.matchChange = {
									type: "remove",
									causeType:
										newAdPonyType!= 0 ?
										"create" : "normal",
									ponyColor: cell.ponyColor
								};
								cell.lockType =  3 ;
								toCreateSrc.push (cellCrd);
								break;
							case  2 :
								cell.matchChange = {
									type: "explode",
									causeType: "normal",
									ponyColor: cell.ponyColor
								};
								cell.lockType =  3 ;
								break;
							case  3 :
								cell.matchChange = {
									type: "shock",
									causeType: "normal",
									ponyColor: cell.ponyColor
								};
								cell.lockType =  3 ;
								break;
							// chroma pony can't be triggered via match
							}
							cell.toCreate = undefined;
						}
						if (newAdPonyType!= 0 )
						{
							// remember if we have not only to remove things
							// but also to create new one
							board[match.recent.x][match.recent.y].toCreate =
							{
								ponyColor: newAdPonyColor,
								ponyType: newAdPonyType,
								src: toCreateSrc
							};
						}
					}

					// at this point, existingMatches will contain all matches
					// whose cells *were not* marked for explosion before this
					// iteration. I. e., they are all the cells that must start
					// exploding/removing exactly now.
					// Also, some cells can be marked with directChange.
					// So, add explosion and creation visualizations for cells
					// within existingMatches according to their set locks and
					// other traits...

					// first, some helpers
					function hitCell (x,y,type,causeType,causeColor,causeX,causeY)
					{
						// trigger a cell removal with a given type: add and
						// begin new visualization of the change, lock cells
						// affected by collateral damage and remember them in
						// new visualization (they are not triggered yet, will
						// be as this visualization reaches "done" point)
						switch (type)
						{
						case "remove":
							var newVis = {
								type: "visRemove", done: false,
								x: x, y: y,
								// "normal"|"explode"|"shock"|"chroma"|"create"
								ponyColor: board[x][y].ponyColor,
								causeColor: causeColor,
								causeType: causeType,
								causeX: causeX,
								causeY: causeY,
								collateral: [] };
							currentVisualizations.addLast (newVis);
							mainLoopOut.push (newVis);
							newRemovals++;
							break;
						case "explode":
							var newVis = {
								type: "visRemoveExplode", done: false,
								x: x, y: y,
								ponyColor: board[x][y].ponyColor,
								causeColor: causeColor,
								causeType: causeType,
								causeX: causeX,
								causeY: causeY,
								collateral: [] },
								collateral = newVis.collateral;
							// trace 8 adjacent cells in 3x3 area around (x,y)
							for (var i=x-1; i<=x+1; i++)
							for (var j=y-1; j<=y+1; j++)
							if (i>=0 && i< 8 &&
								j>=0 && j< 8 &&
								(i!=x||j!=y))
							{
								var cell = board[i][j];
								if (cell.lockType!= 0 ||
									cell.ponyType== 0 )
									// the cell already marked for trigger
									// or is not triggerable
									continue;
								collateral.push ({ x: i, y: j });
								cell.lockType =  3 ;
							}
							currentVisualizations.addLast (newVis);
							mainLoopOut.push (newVis);
							newRemovals++;
							break;
						case "shock":
							var newVis = {
								type: "visRemoveShock", done: false,
								x: x, y: y,
								ponyColor: board[x][y].ponyColor,
								causeColor: causeColor,
								causeType: causeType,
								causeX: causeX,
								causeY: causeY,
								collateral: [] },
								collateral = newVis.collateral;
							// trace other cells in same row and col
							for (var range=1; range< 8 ; range++)
							for each (var crd in [
								{x: x-range, y: y },
								{x: x+range, y: y },
								{x: x, y: y-range },
								{x: x, y: y+range }])
							{
								var i = crd.x,j = crd.y;
								if (i>=0 && i< 8 &&
									j>=0 && j< 8 )
								{
									var cell = board[i][j];
									if (cell.lockType!= 0 ||
										cell.ponyType== 0 )
										// the cell already marked for trigger
										// or is not triggerable
										continue;
									collateral.push ({ x: i, y: j });
									cell.lockType =  3 ;
								}
							}
							currentVisualizations.addLast (newVis);
							mainLoopOut.push (newVis);
							newRemovals++;
							break;
						case "chroma":
							var newVis = {
								type: "visRemoveChroma", done: false,
								x: x, y: y,
								ponyColor: board[x][y].ponyColor,
								causeColor: causeColor,
								causeType: causeType,
								causeX: causeX,
								causeY: causeY,
								collateral: [] },
								collateral = newVis.collateral;
							// trace all cells of causeType color
							for (var i=0; i< 8 ; i++)
							for (var j=0; j< 8 ; j++)
							if (i!=x || j!=y)
							{
								var cell = board[i][j];
								if (cell.lockType!= 0 ||
									cell.ponyType== 0 ||
									cell.ponyColor!=causeColor)
									// the cell already marked for trigger
									// or is not triggerable
									continue;
								collateral.push ({ x: i, y: j });
								cell.lockType =  3 ;
							}
							currentVisualizations.addLast (newVis);
							mainLoopOut.push (newVis);
							newRemovals++;
							break;
						}
					}
					function triggerCell (x,y,causeType,causeColor,causeX,causeY)
					{
						switch (board[x][y].ponyType)
						{
						case  1 :
							hitCell (x,y,"remove",causeType,board[x][y].ponyColor,causeX,causeY);
							break;
						case  2 :
							hitCell (x,y,"explode",causeType,board[x][y].ponyColor,causeX,causeY);
							break;
						case  3 :
							hitCell (x,y,"shock",causeType,board[x][y].ponyColor,causeX,causeY);
							break;
						case  4 :
							hitCell (x,y,"chroma",causeType,causeColor,causeX,causeY);
							break;
						}
					}

					// and then, do it
					for (var i=0; i< 8 ; i++)
					for (var j=0; j< 8 ; j++)
					{
						var cell = board[i][j];
						if (cell.matchChange || cell.directChange ||
							cell.toCreate)
						{
							// this cell is affected
							if (cell.matchChange)
								hitCell (i,j,
									cell.matchChange.type,
									cell.matchChange.causeType,
									cell.matchChange.ponyColor,
									i,j);
							if (cell.directChange)
								hitCell (i,j,
									cell.directChange.type,
									cell.directChange.causeType,
									cell.directChange.ponyColor,
									i,j);
							if (cell.toCreate)
							{
								switch (cell.toCreate.ponyType)
								{
								case  2 :
									var newVis = {
										type: "visCreatePower", done: false,
										x: i, y: j,
										ponyColor: cell.toCreate.ponyColor,
										src: cell.toCreate.src };
									currentVisualizations.addLast (newVis);
									mainLoopOut.push (newVis);
									break;
								case  3 :
									var newVis = {
										type: "visCreateShock", done: false,
										x: i, y: j,
										ponyColor: cell.toCreate.ponyColor,
										src: cell.toCreate.src };
									currentVisualizations.addLast (newVis);
									mainLoopOut.push (newVis);
									break;
								case  4 :
									var newVis = {
										type: "visCreateChroma", done: false,
										x: i, y: j,
										src: cell.toCreate.src };
									currentVisualizations.addLast (newVis);
									mainLoopOut.push (newVis);
									break;
								}
							}
							// remove marks
							cell.matchChange =
							cell.directChange =
							cell.toCreate = null;
						}
					}
					// check existing visualizations, when done - remove
					// and trigger collaterals
					var nextVisNode,wasStable =
						currentVisualizations.count ()>0;
					for (var visNode=currentVisualizations.firstNode ();
						visNode;
						visNode=nextVisNode)
					{
						nextVisNode = visNode.next ();
						var vis = visNode.value;
						if (vis.done)
						{
							// visualization done
							currentVisualizations.remove (visNode);
							switch (vis.type)
							{
							case "visRemove":
								Kbt.patchObject (board[vis.x][vis.y],
								{
									ponyType:  0 ,
									ponyColor:  -1 ,
									lockType:  0 });
								break;
							case "visRemoveExplode":
								for each (coll in vis.collateral)
									triggerCell (coll.x,coll.y,
										"explode",
										vis.causeColor,
										vis.x,vis.y);
								Kbt.patchObject (board[vis.x][vis.y],
								{
									ponyType:  0 ,
									ponyColor:  -1 ,
									lockType:  0 });
								break;
							case "visRemoveShock":
								for each (coll in vis.collateral)
									triggerCell (coll.x,coll.y,
										"shock",
										vis.causeColor,
										vis.x,vis.y);
								Kbt.patchObject (board[vis.x][vis.y],
								{
									ponyType:  0 ,
									ponyColor:  -1 ,
									lockType:  0 });
								break;
							case "visRemoveChroma":
								for each (coll in vis.collateral)
									triggerCell (coll.x,coll.y,
										"chroma",
										vis.causeColor,
										vis.x,vis.y);
								Kbt.patchObject (board[vis.x][vis.y],
								{
									ponyType:  0 ,
									ponyColor:  -1 ,
									lockType:  0 });
								break;
							case "visCreatePower":
								Kbt.patchObject (board[vis.x][vis.y],
								{
									ponyType:  2 ,
									ponyColor: vis.ponyColor,
									lockType:  0 });
								break;
							case "visCreateShock":
								Kbt.patchObject (board[vis.x][vis.y],
								{
									ponyType:  3 ,
									ponyColor: vis.ponyColor,
									lockType:  0 });
								break;
							case "visCreateChroma":
								Kbt.patchObject (board[vis.x][vis.y],
								{
									ponyType:  4 ,
									ponyColor:  -1 ,
									lockType:  0 });
								break;
							}
						}
					}
					// if stable (no more visualizations left), begin fallout
					if (currentVisualizations.count ()<=0)
					{
						// "visFallout": { x: int, y: int, xfrom: int, yfrom: int(m/b<0) }
						for (var x=0; x< 8 ; x++)
						{
							var virtualSrcY = -1; // src y for "yfrom" in case
							// if the new pony falls from above the board
							for (var y= 8 -1; y>=0; y--)
								if (board[x][y].ponyType== 0 )
								{
									// found an empty cell, need to fill:
									for (var prevY=y-1; prevY>=0; prevY--)
										if (board[x][prevY].ponyType!= 0 )
											break;
									if (prevY>=0)
									{
										var thisCell = board[x][y],
											thatCell = board[x][prevY];
										Kbt.patchObject (thisCell,
										{
											ponyType: thatCell.ponyType,
											ponyColor: thatCell.ponyColor,
											lockType:  1 });
										// thatCell will be processed later
										// in the loop
										Kbt.patchObject (thatCell,
										{
											ponyType:  0 ,
											ponyColor:  -1 });
										var newVis = {
											type: "visFallout",
											done: false,
											x: x, y: y,
											xfrom: x, yfrom: prevY };
										currentVisualizations.addLast (newVis);
										mainLoopOut.push (newVis);
									}
									else
									{
										// No ponies above, new pony has to be
										// dropped in. Generate it.
										var newColor =
											Math.floor (Math.random ()* 7 );
										Kbt.patchObject (board[x][y],
										{
											ponyType:  1 ,
											ponyColor: newColor,
											lockType:  1 });
										Math.floor (Math.random ()*7)
										var newVis = {
											type: "visFallout",
											done: false,
											x: x, y: y,
											xfrom: x, yfrom: --virtualSrcY };
										currentVisualizations.addLast (newVis);
										mainLoopOut.push (newVis);
									}
								}
						}
						fallout = currentVisualizations.count ()>0;
						// if it's first fallout of a new game, stabilize gem
						// colors so that there were no ready matches
						if (fallout && newGame)
						{
							newGame = false;
							function colorAtCell (x,y)
							{
								if (x<0 || y<0 ||
									x>= 8 || y>= 8 )
									return  0 ;
								else return board[x][y].ponyColor;
							}
							for (var x=0; x< 8 ; x++)
							for (var y=0; y< 8 ; y++)
							{
								var cell = board[x][y];
								Kbt.assert (cell.ponyColor!= -1 ,
									"first move stabilize: the board is invalidely preset");
								// check if there is a match
								if ((colorAtCell (x-1,y)==cell.ponyColor &&
									colorAtCell (x-2,y)==cell.ponyColor) ||
									(colorAtCell (x+1,y)==cell.ponyColor &&
									colorAtCell (x+2,y)==cell.ponyColor) ||
									(colorAtCell (x,y-1)==cell.ponyColor &&
									colorAtCell (x,y-2)==cell.ponyColor) ||
									(colorAtCell (x,y+1)==cell.ponyColor &&
									colorAtCell (x,y+2)==cell.ponyColor))
								{
									// there is - eliminate it by picking color
									// that doesn't match any from around
									var colorsAround = [];
									for each (var crd in
										[{ x: x-1, y: y },
										{ x: x+1, y: y },
										{ x: x, y: y-1 },
										{ x: x, y: y+1 }])
									if (colorAtCell (crd.x,crd.y)!= -1 )
										colorsAround.push (colorAtCell (crd.x,crd.y));
									REPICK: for (;;)
									{
										var color = Math.floor (Math.random ()*7);
										for each (ca in colorsAround)
											if (ca==color) continue REPICK;
										cell.ponyColor = color;
										break;
									}
								}
							}
						}
					}
					// add score bump for removals
					if (newRemovals)
					{
						mainLoopOut.unshift ({ type: "visNewCascade",
							done: false,
							cascadeCount: ++cascadeCount,
							poniesRemoved: newRemovals
						});
					}
					// if nothing to fall, set latestUpdateId and updateId-s
					// of all cells to 0
					if (currentVisualizations.count ()<=0)
					{
						if (cascadeCount>0 || postFallout)
						{
							mainLoopOut.push ({ type: "visResetCascade",
								done: false });
							cascadeCount = 0;
							postFallout = false;
						}
						latestUpdateId = 0;
						for (var x=0; x< 8 ; x++)
						for (var y=0; y< 8 ; y++)
							board[x][y].updateId = 0;
					}
				}
				else
				{
					// match during fallout - just lock for match
					for each (match in newMatched)
						if (!board[match.x][match.y].matchWhileFallout)
						{
							// request the pending match animation
							mainLoopOut.push ({
								type: "visMarkMatch",
								x: match.x, y: match.y });
							// mark the cell so that not to request the
							// animation for the same cell twice
							Kbt.patchObject (board[match.x][match.y],
							{
								lockType:  2 ,
								matchWhileFallout: true
							});
						}
					// don't care of client to finish them, assume they are
					// instant, board-agnostic or cell-bound and will be
					// replaced by appropriate new visualization of the cell
					// when needed

					// check fallout visualizations, end fallout mode when all
					// of them are over
					var nextVisNode;
					for (var visNode=currentVisualizations.firstNode ();
						visNode;
						visNode=nextVisNode)
					{
						nextVisNode = visNode.next ();
						var vis = visNode.value;
						if (vis.done)
						{
							// visualization done
							currentVisualizations.remove (visNode);
							switch (vis.type)
							{
							case "visFallout":
								Kbt.patchObject (board[vis.x][vis.y],
								{
									lockType:  0 });
								break;
							// other cases are not possible here yet, but let
							// the structure be just for case
							}
						}
					}
					fallout = currentVisualizations.count ()>0;
					if (!fallout)
					{
						// when fallout ended, clear pending matches marks
						for (var x=0; x< 8 ; x++)
						for (var y=0; y< 8 ; y++)
							board[x][y].updateId = 0;
						postFallout = true;
					}
				}
			}
		}
	}

	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}this.mainLoop = new mainLoop ();
	this.markToDispose(kbtBind(this,function(){ do  { if (me.mainLoop) { (me.mainLoop).dispose (); me.mainLoop= null; } } while (0) ; }

	));this.performIdle = function ()
	{
		this.mainLoop ({ type: "idle" });
		return mainLoopOut;
	};

	this.performMove = function (move)
	{
		this.mainLoop (move);
		return mainLoopOut;
	};

	this.newGame = function ()
	{
		this.mainLoop.dispose ();
		this.mainLoop = new mainLoop (true);

		mainLoopOut.length = 0;
		for (var x=0; x< 8 ; x++)
		for (var y=0; y< 8 ; y++)
		{
			Kbt.patchObject (board[x][y],
			{
				ponyColor:  -1 ,
				ponyType:  0 ,
				lockType:  0 ,
				matchId: -1,
				directChange: null,
				matchChange: null,
				toCreate: null,
				causedBy: null,
				updateId: 0
			});
		}
	};
}
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}


//
// scr_game.kbtjsx: screenman.kbtjsx, morewidgets.kbtjsx, resources.kbtjsx
// Game screen
//

var
	tipPowerPonyShown = false,
	tipShockPonyShown = false,
	tipChromaFaceShown = false;
  function ScreenGame ()
{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
	this.mxEnable (MxScreen);
	var
		me = this,
		myGuiHost = this.guiHost (),
		bgPicture = imageClips.BG_GAME,
		picBoard = imageClips.GAME_BOARD,
		layerBehindBoard = this.markToDispose((new SpriteLayer ())),
		layerOnBoard = this.markToDispose((new SpriteLayer ())),
		layerOnBoardFX = this.markToDispose((new SpriteLayer ())),
		layerInFrontOfBoard = this.markToDispose((new SpriteLayer ())),
		layerFX = this.markToDispose((new SpriteLayer ())),
		layerGameCtl = this.markToDispose((new SpriteLayer ()));

	var
		ammoLeft = 16,
		ratioBase = 1,
		ratioPlusBase = 9,
		ratioPlus = ratioPlusBase,
		// fractional, +ratioBase/(ratioBase+ratioPlus) per 1 pony collected
		recycleProgress = 0.0,
		score = 0,
		gameOverCheckTimeout = 0,
		boardStable = true;

	var
		game = new Game (),
		curBoardX = -1,
		curBoardY = -1,
		clipX = -1;
	game.newGame ();

	var
		evtPowerPonyTip =
			this.markToDispose((makeStaticEvent ({ eventType: "powerPonyTip" }))),
		evtShockPonyTip =
			this.markToDispose((makeStaticEvent ({ eventType: "shockPonyTip" }))),
		evtChromaFaceTip =
			this.markToDispose((makeStaticEvent ({ eventType: "chromaFaceTip" }))),
		evtCheckForGameOver =
			this.markToDispose((makeStaticEvent ({ eventType: "checkForGameOver" })));

	var
		labelScore = Kbt.multiApply (this.markToDispose((new WtLabel ())),
		{
			setFontDesc: { bold: true, italic: false }, 
			setColor: "#FFFFFF",
			setParent: myGuiHost,
			setRect: Layouts.screenGame.labelScore,
			setAutoLines: 1,
			setAligns: { h: "right" },
			enableBitmapCache: true
		}),
		labelRecycleRate = Kbt.multiApply (this.markToDispose((new WtLabel ())),
		{
			setFontDesc: { bold: true, italic: false }, 
			setColor: "#FFFFFF",
			setParent: myGuiHost,
			setRect: Layouts.screenGame.labelRecycleRate,
			setAutoLines: 1,
			setAligns: { h: "left" },
			enableBitmapCache: true
		}),
		labelAmmoLeft = Kbt.multiApply (this.markToDispose((new WtLabel ())),
		{
			setFontDesc: { bold: true, italic: false }, 
			setColor: "#FFFFFF",
			setParent: myGuiHost,
			setRect: Layouts.screenGame.labelAmmoLeft,
			setAutoLines: 1,
			setAligns: { h: "right" },
			enableBitmapCache: true
		});

	function updateScore ()
	{
		labelScore.setText ("PUNKTE: "+score);
	}

	function updateRecycleRate ()
	{
		labelRecycleRate.setText ("FAKTOR: +"+ratioBase+" / "+
			(ratioBase+ratioPlus));
	}

	function updateAmmoLeft ()
	{
		if (ammoLeft>0)
		{
			labelAmmoLeft.setText ("MUNITION: "+ammoLeft);
			labelAmmoLeft.setColor ("#FFFFFF");
		}
		else
		{
			labelAmmoLeft.setText ("! LETZTEN CLIP !");
			labelAmmoLeft.setColor ("#FF0000");
		}
	}

	updateScore ();
	updateRecycleRate ();
	updateAmmoLeft ();

	this.markToDispose((new function ()
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		// ammo label flasher
		_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
		var show = true;
		for (;;)
		{
			do {let func=new(appEQ.sleep )(500),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			labelAmmoLeft.setVisible (ammoLeft>0 || show);
			show = !show;
		}
	} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}()));

	//
	// game input handling object
	//
	var gameInput = Kbt.multiApply (this.markToDispose((new   function ()
	{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
		this.mxEnable (MxWidget);
		var clipDrag = false,clipDragBaseX;
		var base = this.mxOverride (MxWidget,
		{
			onPointMessage: function (pointRel,message)
			{
				// board control points
				var bx1 = Layouts.screenGame.boardRect.x,
					by1 = Layouts.screenGame.boardRect.y,
					bx2 = bx1+Layouts.screenGame.boardRect.width,
					by2 = by1+Layouts.screenGame.boardRect.height,
					bcw = Layouts.screenGame.boardRect.width/ 8 ,
					bch = Layouts.screenGame.boardRect.height/ 8 ;
				// clip control points
				var ccw = Layouts.screenGame.clipCellWidth,
					cch = Layouts.screenGame.clipCellHeight,
					cy1 = Layouts.screenGame.clipCenterY,
					cy2 = cy1+cch;
				switch (message.eventType)
				{
				case "touchDown":
					if (pointRel.y>=cy1 && pointRel.y<=cy2)
					{
						clipDrag = true;
						clipDragBaseX = pointRel.x;
						appEQ.post ({ eventType: "clipDragBegin" });
					}
					else if (pointRel.x>=bx1 && pointRel.x<bx2 &&
							pointRel.y>=by1 && pointRel.y<by2)
					{
						// click on the board - indicate attempt to shoot
						curBoardX = Math.floor ((pointRel.x-bx1)/bcw);
						curBoardY = Math.floor ((pointRel.y-by1)/bch);
						appEQ.post ({ eventType: "tryShoot" });
					}
					return true;
				case "touchUp":
				case "touchReset":
					if (clipDrag)
					{
						clipDrag = false;
						appEQ.post ({ eventType: "clipDragEnd" });
					}
					return true;
				case "touchMove":
					if (clipDrag)
					{
						// dragging clip
						appEQ.post ({ eventType: "clipDragMove",
							deltaX: pointRel.x-clipDragBaseX });
					}
					else
					{
						// not dragging clip - check board target coords
						
						if (pointRel.x<bx1 || pointRel.x>=bx2 ||
							pointRel.y<by1 || pointRel.y>=by2)
							curBoardX = curBoardY = -1;
						else
						{
							curBoardX = Math.floor ((pointRel.x-bx1)/bcw);
							curBoardY = Math.floor ((pointRel.y-by1)/bch);
						}
					}
					return false;
				}
				return base.onPointMessage (pointRel,message);
			}
		});
	}catch(e){this.markToDispose(function(){throw e;});this.dispose();}})),
	{
		setParent: myGuiHost,
		setRect: new Rect (0,0, 480 , 640 )
	});

	function renderScreen (gc)
	{
		// draw BG
		gc.drawSubimage (new Point (0,0),bgPicture);

		layerBehindBoard.render (gc);
		{let _Kbt$as=new _Kbt$AutoScope();try
		{
			_Kbt$as.markToDispose((saveGCState (gc)));
			gc.beginPath ();
			gc.rect (Layouts.screenGame.boardRect.x,
				Layouts.screenGame.boardRect.y,
				Layouts.screenGame.boardRect.width,
				Layouts.screenGame.boardRect.height);
			gc.clip ();
			gc.drawSubimage (Layouts.screenGame.boardRect.leftTop (),
				picBoard);
			layerOnBoard.render (gc);
			layerOnBoardFX.render (gc);
		}
		catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}layerInFrontOfBoard.render (gc);
		layerFX.render (gc);
		layerGameCtl.render (gc);
		// TODO: status
	}

	//
	// visual FX toolkit
	//
	function playSplat (at,color,layer)
	{
		new function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
			var sprite = Kbt.multiApply (_Kbt$as.markToDispose((new Sprite ())),
			{
				setLayer: layer,
				setAnimator: animations.splat (color),
				setRefPoint: at
			});

			do {let func=new(appEQ.sleep )(250),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
		} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
	}

	function playZap (at,color,layer)
	{
		new function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
			var sprite = Kbt.multiApply (_Kbt$as.markToDispose((new Sprite ())),
			{
				setLayer: layer,
				setAnimator: animations.zap (color),
				setRefPoint: at
			});

			do {let func=new(appEQ.sleep )(750),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
		} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
	}

	function playExplode (at,layer)
	{
		new function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
			var sprite = Kbt.multiApply (_Kbt$as.markToDispose((new Sprite ())),
			{
				setLayer: layer,
				setAnimator: animations.explode,
				setRefPoint: at
			});

			do {let func=new(appEQ.sleep )(500),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
		} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
	}

	var gravAcc = new Point (0,700),
		poniesOnTheFly = 0;
	function throwPony (from,velocity,color)
	{
		Kbt.assert (color>=0 && color< 7 ,"WTF!");
		new function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			poniesOnTheFly++;
			_Kbt$as.markToDispose(kbtBind(this,function(){ poniesOnTheFly--; }

			));_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
			var sprite = Kbt.multiApply (_Kbt$as.markToDispose((new Sprite ())),
				{
					setLayer: layerInFrontOfBoard,
					setRefPoint: from,
					setFrame: sprites.coloredPony (color, 2 )
				}),
				mod = sprite.addMods ();

			var tick,finitaLaComedia = false;
			 do { tick = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(tick)(tick===appFrameTimer.tick ())
			),false,kbtBind (this,function(tick){
				var newRefPoint = sprite.getRefPoint ()
					.add (velocity.mul (appLastFrameElapsed*0.001));
				sprite.setRefPoint (newRefPoint);
				velocity = velocity.add (
					gravAcc.mul (appLastFrameElapsed*0.001));

				mod.rotation = Math.atan2 (velocity.y,velocity.x)
					+1.5707963267948966 ;

				var yThreshold =
					Layouts.screenGame.boardRect.rightBottom ().y;
				if (newRefPoint.y>yThreshold)
				{
					mod.alpha = 1.0-
						(newRefPoint.y-yThreshold)/
						( 640 -yThreshold);
					if (mod.alpha<0) mod.alpha = 0;
				}
				if (newRefPoint.x<-240 ||
					newRefPoint.x>=720 ||
					newRefPoint.y<-320 ||
					newRefPoint.y>640 )
					// pony off screen, finita la comedia
					finitaLaComedia = true;
			}));_Kbt$as.markToDispose(tick);} while (false);

			for (; !finitaLaComedia;)
			{
				do {let func=new(appEQ.sleep )(1000),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			}
		} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
	}

	//
	// game animations visualizer
	//
	var boardPonySprites = [];
	for (var i=0; i< 8 ; i++)
	{
		let bbCol = [];
		boardPonySprites.push (bbCol);
		for (var j=0; j< 8 ; j++)
			bbCol.push (null);
	}
	// cleanupper
	this.markToDispose(kbtBind(this,function(){
		for (var i=0; i< 8 ; i++)
		for (var j=0; j< 8 ; j++)
			do  { if (boardPonySprites[i][j]) { (boardPonySprites[i][j]).dispose (); boardPonySprites[i][j]= null; } } while (0) ;
	}

	));function getCellCenterOnScreen (x,y)
	{
		// helper
		var bx1 = Layouts.screenGame.boardRect.x,
			by1 = Layouts.screenGame.boardRect.y,
			bcw = Layouts.screenGame.boardRect.width/ 8 ,
			bch = Layouts.screenGame.boardRect.height/ 8 ;
		return new Point (bx1+(x+0.5)*bcw,by1+(y+0.5)*bch);
	}

	function getPonyAnimator (ponyType,ponyColor)
	{
		switch (ponyType)
		{
		case  1 :
		default:
			return animations.genPony (ponyColor);
		case  2 :
			return animations.powerPony (ponyColor);
		case  3 :
			return animations.shockPony (ponyColor);
		case  4 :
			return animations.chromaFace;
		}
	}

	function playVisFallouts (visFallouts)
	{
		// "visFallout": { x: int, y: int, xfrom: int, yfrom: int(m/b<0) }
		var prevBoardPonySprites = [];
		for (var i=0; i< 8 ; i++)
		{
			let bbCol = [];
			prevBoardPonySprites.push (bbCol);
			for (var j=0; j< 8 ; j++)
				bbCol.push (boardPonySprites[i][j]);
		}

		for each (visFallout in visFallouts)
		{
			let newPonySprite = visFallout.yfrom>=0?
				prevBoardPonySprites[visFallout.xfrom][visFallout.yfrom]
				: Kbt.multiApply (new Sprite (),
				{
					setLayer: layerOnBoard,
					setAnimator: getPonyAnimator (
						game.board[visFallout.x][visFallout.y].ponyType,
						game.board[visFallout.x][visFallout.y].ponyColor)
				});
			boardPonySprites[visFallout.x][visFallout.y] = newPonySprite;

			new function (visFallout)
			{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
				_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
				var bch = Layouts.screenGame.boardRect.height/ 8 ;

				newPonySprite.sendMsgToAnimator ({ eventType: "setFall" });
				var tweenDone,tweenFall = _Kbt$as.markToDispose((new Tweener ({
					from: getCellCenterOnScreen (visFallout.xfrom,visFallout.yfrom),
					to: getCellCenterOnScreen (visFallout.x,visFallout.y),
					over: (visFallout.y-visFallout.yfrom)*125,
					useFunction: TweenFunc.quadric,
					easingMode: "easein",
					onChanged: function (p)
					{
						newPonySprite.setRefPoint (p)
					}
				})));
				do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[tweenDone] = [_Kbt$msg];_Kbt$consumed = true; if(tweenDone===tweenFall.eventCompleted ()){;break;}else _Kbt$consumed = false;}} while (false);
				newPonySprite.sendMsgToAnimator ({ eventType: "setStand" });
				visFallout.done = true;
			} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}(visFallout);
		}
	}

	function playVisMoveResult (visMoveOks)
	{
		// "visMoveOk": { changes: [{ x: int, y: int, prevColor: int, newColor: int,
		//  prevType: int, newType: int }] } // the only reply element if present
		for each (visMoveOk in visMoveOks)
		{
			for each (change in visMoveOk.changes)
			{
				do  { if (boardPonySprites[change.x][change.y]) { (boardPonySprites[change.x][change.y]).dispose (); boardPonySprites[change.x][change.y]= null; } } while (0) ;
				boardPonySprites[change.x][change.y] =
					Kbt.multiApply (new Sprite (),
					{
						setLayer: layerOnBoard,
						setAnimator: getPonyAnimator (
							change.newType,
							change.newColor),
						setRefPoint: getCellCenterOnScreen (change.x,change.y)
					});
			}
		}
	}

	function playVisMarkMatch (visMarkMatch)
	{
		// "visMarkMatch": { x: int, y: int }
		var bsp = boardPonySprites[visMarkMatch.x][visMarkMatch.y];
		if (bsp) bsp.setAlpha (0.5);
	}

	var ponyThrowVelocity = 80;
	function playVisRemove (visRemove)
	{
		// "visRemove": { x: int, y: int,
		//  ponyColor: int, // (but the board cell still holds the pony)
		//  causeType: "normal"|"explode"|"shock"|"chroma"|"create",
		//  causeX: int, causeY: int }
		switch (visRemove.causeType)
		{
		case "create":
			// don't remove this item here, it'll be removed by creation
			// visualizer
			visRemove.done = true;
			break;
		case "normal":
			do  { if (boardPonySprites[visRemove.x][visRemove.y]) { (boardPonySprites[visRemove.x][visRemove.y]).dispose (); boardPonySprites[visRemove.x][visRemove.y]= null; } } while (0) ;
			throwPony (getCellCenterOnScreen (visRemove.x,visRemove.y),
				new Point (0,0),
				game.board[visRemove.x][visRemove.y].ponyColor);
			visRemove.done = true;
			break;
		case "explode":
		case "shock":
			do  { if (boardPonySprites[visRemove.x][visRemove.y]) { (boardPonySprites[visRemove.x][visRemove.y]).dispose (); boardPonySprites[visRemove.x][visRemove.y]= null; } } while (0) ;
			throwPony (getCellCenterOnScreen (visRemove.x,visRemove.y),
				getCellCenterOnScreen (visRemove.x,visRemove.y)
					.sub (getCellCenterOnScreen (visRemove.causeX,visRemove.causeY))
					.normalize (0.001)
					.mul (ponyThrowVelocity),
				game.board[visRemove.x][visRemove.y].ponyColor);
			visRemove.done = true;
			break;
		case "chroma":
			new function ()
			{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
				// TODO (opt): play FX of ray/spark hitting from
				// visRemove.causeX,visRemove.causeY into here
				_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
				do {let func=new(appEQ.sleep )(Math.random (1000)),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
				playSplat (getCellCenterOnScreen (visRemove.x,visRemove.y),
					game.board[visRemove.x][visRemove.y].ponyColor,
					layerFX);
				do  { if (boardPonySprites[visRemove.x][visRemove.y]) { (boardPonySprites[visRemove.x][visRemove.y]).dispose (); boardPonySprites[visRemove.x][visRemove.y]= null; } } while (0) ;
				throwPony (getCellCenterOnScreen (visRemove.x,visRemove.y),
					getCellCenterOnScreen (visRemove.x,visRemove.y)
						.sub (getCellCenterOnScreen (visRemove.causeX,visRemove.causeY))
						.normalize (0.001)
						.mul (ponyThrowVelocity),
					game.board[visRemove.x][visRemove.y].ponyColor);
				visRemove.done = true;
			} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
			break;
		}
	}

	function playVisRemoveExplode (visRemoveExplode)
	{
		// "visRemoveExplode": { x: int, y: int, causeColor: int, causeType: ..., collateral: [{ x: int, y: int }] }
		do  { if (boardPonySprites[visRemoveExplode.x][visRemoveExplode.y]) { (boardPonySprites[visRemoveExplode.x][visRemoveExplode.y]).dispose (); boardPonySprites[visRemoveExplode.x][visRemoveExplode.y]= null; } } while (0) ;
		new function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
			playExplode (getCellCenterOnScreen (visRemoveExplode.x,visRemoveExplode.y),
				layerFX);
			do {let func=new(appEQ.sleep )(250),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			visRemoveExplode.done = true;
		} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
	}

	function playVisRemoveShock (visRemoveShock)
	{
		// "visRemoveShock": { x: int, y: int, causeColor: int, causeType: ..., collateral: [{ x: int, y: int }] }	
		do  { if (boardPonySprites[visRemoveShock.x][visRemoveShock.y]) { (boardPonySprites[visRemoveShock.x][visRemoveShock.y]).dispose (); boardPonySprites[visRemoveShock.x][visRemoveShock.y]= null; } } while (0) ;
		new function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
			playZap (getCellCenterOnScreen (visRemoveShock.x,visRemoveShock.y),
				game.board[visRemoveShock.x][visRemoveShock.y].ponyColor,
				layerOnBoardFX);
			do {let func=new(appEQ.sleep )(250),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			visRemoveShock.done = true;
		} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
	}

	function playVisRemoveChroma (visRemoveChroma)
	{
		// "visRemoveChroma": { x: int, y: int, causeColor: int, causeType: ..., collateral: [{ x: int, y: int }] }
		new function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			// TODO (opt): play chroma blast and shake ponies from collateral
			_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
			for each (coll in visRemoveChroma.collateral)
			{
				var bsp = boardPonySprites[visRemoveChroma.x][visRemoveChroma.y];
				if (bsp) bsp.sendMsgToAnimator ({ eventType: "setFall" });
			}
			do {let func=new(appEQ.sleep )(250),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			do  { if (boardPonySprites[visRemoveChroma.x][visRemoveChroma.y]) { (boardPonySprites[visRemoveChroma.x][visRemoveChroma.y]).dispose (); boardPonySprites[visRemoveChroma.x][visRemoveChroma.y]= null; } } while (0) ;
			visRemoveChroma.done = true;
		} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
	}

	function playPoniesCollapse (x,y,srcs,color)
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		// srcs = [{ x: int, y: int }]
		var delay = 500,
			completed = _Kbt$as.markToDispose((new SimpleBarrier ()));
		for each (src in srcs)
		{
			let bsp = boardPonySprites[src.x][src.y];
			let dx = src.x,dy = src.y;
			if (bsp)
			{
				let newOpDone = completed.newOperation ();
				bsp.setAlpha (0.5);
				var twn = _Kbt$as.markToDispose((new Tweener ({
					from: getCellCenterOnScreen (src.x,src.y),
					to: getCellCenterOnScreen (x,y),
					over: delay,
					useFunction: TweenFunc.quadric,
					onChanged: function (p)
					{
						bsp.setRefPoint (p);
					}
				})));
				twn.setOnCompleted (function ()
				{
					do  { if (boardPonySprites[dx][dy]) { (boardPonySprites[dx][dy]).dispose (); boardPonySprites[dx][dy]= null; } } while (0) ;
					newOpDone ();
				});
			}
		}
		var comp;
		if (completed.opsTotal ()>0)
			do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[comp] = [_Kbt$msg];_Kbt$consumed = true; if(comp===completed){;break;}else _Kbt$consumed = false;}} while (false);
		playSplat (getCellCenterOnScreen (x,y),color,layerFX);
	}

	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}function playVisCreatePower (visCreatePower)
	{
		// "visCreatePower": { x: int, y: int, ponyColor: int, src: [{ x: int, y: int }] }
		new function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
			do {let func=new(playPoniesCollapse )(visCreatePower.x,visCreatePower.y,
				visCreatePower.src,visCreatePower.ponyColor),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			do  { if (boardPonySprites[visCreatePower.x][visCreatePower.y]) { (boardPonySprites[visCreatePower.x][visCreatePower.y]).dispose (); boardPonySprites[visCreatePower.x][visCreatePower.y]= null; } } while (0) ;
			boardPonySprites[visCreatePower.x][visCreatePower.y] =
				Kbt.multiApply (new Sprite (),
				{
					setLayer: layerOnBoard,
					setAnimator: getPonyAnimator (
						 2 ,
						visCreatePower.ponyColor),
					setRefPoint: getCellCenterOnScreen
						(visCreatePower.x,visCreatePower.y)
				});
			visCreatePower.done = true;

			// send tip request
			evtPowerPonyTip.withdraw ();
			appEQ.postStaticPersistent (evtPowerPonyTip);
		} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
	}

	function playVisCreateShock (visCreateShock)
	{
		// "visCreateShock": { x: int, y: int, ponyColor: int, src: [{ x: int, y: int }] }
		new function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
			do {let func=new(playPoniesCollapse )(visCreateShock.x,visCreateShock.y,
				visCreateShock.src,visCreateShock.ponyColor),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			do  { if (boardPonySprites[visCreateShock.x][visCreateShock.y]) { (boardPonySprites[visCreateShock.x][visCreateShock.y]).dispose (); boardPonySprites[visCreateShock.x][visCreateShock.y]= null; } } while (0) ;
			boardPonySprites[visCreateShock.x][visCreateShock.y] =
				Kbt.multiApply (new Sprite (),
				{
					setLayer: layerOnBoard,
					setAnimator: getPonyAnimator (
						 3 ,
						visCreateShock.ponyColor),
					setRefPoint: getCellCenterOnScreen
						(visCreateShock.x,visCreateShock.y)
				});
			visCreateShock.done = true;

			// send tip request
			evtShockPonyTip.withdraw ();
			appEQ.postStaticPersistent (evtShockPonyTip);
		} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
	}

	function playVisCreateChroma (visCreateChroma)
	{
		// "visCreateChroma": { x: int, y: int, src: [{ x: int, y: int }] }
		new function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
			do {let func=new(playPoniesCollapse )(visCreateChroma.x,visCreateChroma.y,
				visCreateChroma.src,
				game.board[visCreateChroma.x][visCreateChroma.y].ponyColor),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			do  { if (boardPonySprites[visCreateChroma.x][visCreateChroma.y]) { (boardPonySprites[visCreateChroma.x][visCreateChroma.y]).dispose (); boardPonySprites[visCreateChroma.x][visCreateChroma.y]= null; } } while (0) ;
			boardPonySprites[visCreateChroma.x][visCreateChroma.y] =
				Kbt.multiApply (new Sprite (),
				{
					setLayer: layerOnBoard,
					setAnimator: getPonyAnimator (
						 4 ,
						 -1 ),
					setRefPoint: getCellCenterOnScreen
						(visCreateChroma.x,visCreateChroma.y)
				});
			visCreateChroma.done = true;

			// send tip request
			evtChromaFaceTip.withdraw ();
			appEQ.postStaticPersistent (evtChromaFaceTip);
		} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
	}

	//
	// ponies clip control and visualizer
	//
	var poniesInClip = [], // colors
		currentInClip = -1, // index
		outOfAmmo = this.markToDispose((makeStaticEvent ({ eventType: "outOfAmmo" })));

	var moveInProgress = false;
	var poniesClipControl = this.markToDispose((new function ()
	{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
		_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));

		var spriteCrosshair = _Kbt$as.markToDispose((new Sprite ())),
			spriteMod = spriteCrosshair.addMods (),
			spriteBoot = Kbt.multiApply (_Kbt$as.markToDispose((new Sprite ())),
			{
				setLayer: layerGameCtl,
				setRefPoint: new Point (Layouts.screenGame.clipCenterX,
					Layouts.screenGame.clipCenterY+
					Layouts.screenGame.clipCellHeight),
				setFrame: sprites.boot
			}),
			prevBoardX = -1,prevBoardY = -1;

		var frameTick,ft = 0,angle = 0;
		 do { frameTick = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(frameTick)(frameTick===appFrameTimer.tick ())
		),false,kbtBind (this,function(frameTick){
			angle += appLastFrameElapsed*0.002;
			if (angle<0) angle += 6.283185307179586 ;
			if (angle>6.283185307179586 ) angle -= 6.283185307179586 ;
			spriteMod.rotation = angle;
			spriteMod.scale.x = spriteMod.scale.y =
				Math.max (1,1.16*Math.sin (angle*4));
			if (curBoardX!=prevBoardX || curBoardY!=prevBoardY)
			{
				prevBoardX = curBoardX;
				prevBoardY = curBoardY;

				var bx1 = Layouts.screenGame.boardRect.x,
					by1 = Layouts.screenGame.boardRect.y,
					bcw = Layouts.screenGame.boardRect.width/ 8 ,
					bch = Layouts.screenGame.boardRect.height/ 8 ;

				if (curBoardX==-1 || curBoardY==-1)
					spriteCrosshair.setLayer (null);
				else
				{
					var cell = game.board[curBoardX][curBoardY];
					Kbt.multiApply (spriteCrosshair,
					{
						setLayer: layerGameCtl,
						setRefPoint: new Point (bx1+bcw*(curBoardX+0.5),
							by1+bch*(curBoardY+0.5)),
						setFrame:
							sprites.crosshair (
								cell.lockType== 0 &&
								cell.ponyType!= 0 )
					});
				}
			}
		}));_Kbt$as.markToDispose(frameTick);} while (false);

		function suspendCrosshair ()
		{
			return frameTick.suspend ();
		}

		// pony sprites
		var clipPonySprites = [],clipPonyMods = [],
			clipNotInteractive = false;
		function clearClipSprites ()
		{
			for each (cbs in clipPonySprites) cbs.dispose ();
			clipPonySprites.length = 0;
			clipPonyMods.length = 0;
		}
		_Kbt$as.markToDispose(kbtBind(this,function(){ clearClipSprites (); }

		));var clipPlacedAtX = 0;
		function placeClipSprites (x)
		{
			var ccw = Layouts.screenGame.clipCellWidth,
			cch = Layouts.screenGame.clipCellHeight,
			cx = Layouts.screenGame.clipCenterX,
			cy1 = Layouts.screenGame.clipCenterY,
			cy2 = cy1+cch;

			// determine selected pony
			var i = 0;
			var current = Math.round ((cx-x)/ccw);
			if (!clipNotInteractive)
			if (current>=0 && current<poniesInClip.length)
				currentInClip = current;
			else
				currentInClip = current<0? 0 : poniesInClip.length-1;
			// and update positions
			for each (cbs in clipPonySprites)
			{
				Kbt.multiApply (cbs,
				{
					setRefPoint: new Point (
						x+ccw*i,
						(cy1+cy2)*0.5)
				});
				var current = Math.round ((cx-x)/ccw);
				if (!clipNotInteractive && current==i)
					clipPonyMods[i].scale = Layouts.screenGame.selectedPonyScale;
				else
					clipPonyMods[i].scale = new Point (1,1);
				i++;
			}
			clipPlacedAtX = x;
		}

		function placeClipSpritesWithDrop (x,idxToDrop,dropPhase)
		{
			var ccw = Layouts.screenGame.clipCellWidth,
			cch = Layouts.screenGame.clipCellHeight,
			cx = Layouts.screenGame.clipCenterX,
			cy1 = Layouts.screenGame.clipCenterY,
			cy2 = cy1+cch,
			delta = dropPhase*ccw*0.5;

			// update positions
			var i = 0;
			for each (cbs in clipPonySprites)
			{
				if (i!=idxToDrop)
				Kbt.multiApply (cbs,
				{
					setRefPoint: new Point (
						x+ccw*i+(i<idxToDrop? delta : -delta),
						(cy1+cy2)*0.5)
				});
				i++;
			}
			clipPlacedAtX = x;
		}

		function moveClipSprites (cxFrom,cxTo)
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			let move = _Kbt$as.markToDispose((new Tweener (
			{
				from: cxFrom,
				to: cxTo,
				// distance all over the screen will take 500 ms, smaller one
				// will take proportionally less
				over: 500*Math.abs (cxTo-cxFrom)/ 480 ,
				useFunction: TweenFunc.cubic,
				onChanged:  kbtBind(this,function(d)
				{ placeClipSprites (d); }
			)})));
			let moveDone;
			do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[moveDone] = [_Kbt$msg];_Kbt$consumed = true; if(moveDone==move.eventCompleted ()){;break;}else _Kbt$consumed = false;}} while (false);
		}

		catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}function clipWidth ()
		{
			return (poniesInClip.length-1)*Layouts.screenGame.clipCellWidth;
		}

		// controller main loop
		for (;;)
		{
			let msgClipRecharge,
				msgClipDragBegin,
				msgShoot;

			do {
				for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[msgClipRecharge,msgClipDragBegin,msgShoot] = [_Kbt$msg,_Kbt$msg,_Kbt$msg];_Kbt$consumed = true;
					if(msgClipRecharge.eventType=="clipRecharge") {[msgClipDragBegin,msgShoot] = [undefined,undefined];break;}else 
				
					if(msgClipDragBegin.eventType=="clipDragBegin") {[msgClipRecharge,msgShoot] = [undefined,undefined];break;}else 
				
					if(msgShoot.eventType=="shoot")
				{[msgClipRecharge,msgClipDragBegin] = [undefined,undefined];break;}else _Kbt$consumed = false;}} while (false);

			if (msgClipRecharge)
			{let _Kbt$as=new _Kbt$AutoScope();try
			{
				// clip recharge
				// { eventType: "msgClipRecharge", ponies: [] }

				// set new ponies and prepare for recharge animation
				poniesInClip = msgClipRecharge.ponies;
				_Kbt$as.markToDispose((suspendCrosshair ()));
				// TODO: suspend boot

				// create new sprites
				clearClipSprites ();
				for each (ponyColor in poniesInClip)
				{
					var sprite = Kbt.multiApply (new Sprite (),
						{
							setAnimator: animations.genPony (ponyColor),
							setLayer: layerGameCtl
						});
					clipPonySprites.push (sprite);
					clipPonyMods.push (sprite.addMods ());
					sprite.sendMsgToAnimator ({ eventType: "setKnockout" });
				}

				// new current pony is first in clip
				currentInClip = 0;

				// play clip in animation
				clipNotInteractive = true;
				var ccw = Layouts.screenGame.clipCellWidth,
					cxTo = Layouts.screenGame.clipCenterX,
					cxFrom = -(poniesInClip.length*ccw);
				do {let func=new(moveClipSprites )(cxFrom,cxTo),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
				clipNotInteractive = false;
				placeClipSprites (cxTo);
			}
			catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}else if (msgClipDragBegin)
			{let _Kbt$as=new _Kbt$AutoScope();try
			{
				// begin dragging clip
				// TODO: suspend boot

				var
					cx = Layouts.screenGame.clipCenterX,
					ccw = Layouts.screenGame.clipCellWidth,
					// current x of the whole clip (i. e., of pony #0)
					xbase = cx-currentInClip*ccw,
					xcurrent = xbase;
				for (;;)
				{
					let msgClipDragMove,msgClipDragEnd;
					do {
						for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[msgClipDragMove,msgClipDragEnd] = [_Kbt$msg,_Kbt$msg];_Kbt$consumed = true;
							if(msgClipDragMove.eventType=="clipDragMove") {[msgClipDragEnd] = [undefined];break;}else 
						
							if(msgClipDragMove.eventType=="clipDragEnd"){[msgClipDragMove] = [undefined];break;}else _Kbt$consumed = false;}} while (false);
					if (msgClipDragEnd)
					{let _Kbt$as=new _Kbt$AutoScope();try
					{
						// play snap clip to current ponies animation
						var
							cxTo = Layouts.screenGame.clipCenterX-ccw*currentInClip,
							cxFrom = xcurrent;
						do {let func=new(moveClipSprites )(cxFrom,cxTo),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
						break;
					}
					catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}else // msgClipDragMove
						placeClipSprites (xcurrent = xbase+msgClipDragMove.deltaX);
				}
			}
			catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}else if (msgShoot && poniesInClip.length>0)
			{let _Kbt$as=new _Kbt$AutoScope();try
			{
				// TODO: suspend boot

				Kbt.assert (currentInClip!=-1,"invalid current pony!");
				var ponyColor = poniesInClip[currentInClip];

				moveInProgress = true;
				_Kbt$as.markToDispose(kbtBind(this,function(){ moveInProgress = false; }

				// try perform a move
				));let bootInPoint = new Point (Layouts.screenGame.clipCenterX,
						Layouts.screenGame.clipCenterY+
						Layouts.screenGame.clipCellHeight),
					bootOutPoint = new Point (bootInPoint.x,
						 640 ),
					shootAtX = curBoardX,
					shootAtY = curBoardY;
				var result = game.performMove ({ type: "move",
					changes: [{
						x: shootAtX, y: shootAtY,
						ponyColor: ponyColor,
						ponyType:  1 }]});
				if (!result.length || result[0].type=="visMoveReject")
					continue;

				let tweenDone;
				// play boot swing
				let swingOut = _Kbt$as.markToDispose((new Tweener (
				{
					from: bootInPoint,
					to: bootOutPoint,
					over: 250,
					useFunction: TweenFunc.cubic,
					easingMode: "easeout",
					onChanged:  kbtBind(this,function(p)
					{ spriteBoot.setRefPoint (p); }
				)})));
				do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[tweenDone] = [_Kbt$msg];_Kbt$consumed = true; if(tweenDone==swingOut.eventCompleted ()){;break;}else _Kbt$consumed = false;}} while (false);

				let swingIn = _Kbt$as.markToDispose((new Tweener (
				{
					from: bootOutPoint,
					to: bootInPoint,
					over: 250,
					useFunction: TweenFunc.back (0.25),
					easingMode: "easein",
					onChanged:  kbtBind(this,function(p)
					{ spriteBoot.setRefPoint (p); }
				)})));
				do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[tweenDone] = [_Kbt$msg];_Kbt$consumed = true; if(tweenDone==swingIn.eventCompleted ()){;break;}else _Kbt$consumed = false;}} while (false);

				var bx1 = Layouts.screenGame.boardRect.x,
					by1 = Layouts.screenGame.boardRect.y,
					bcw = Layouts.screenGame.boardRect.width/ 8 ,
					bch = Layouts.screenGame.boardRect.height/ 8 ;

				// remove sprite of current pony
				clipPonySprites[currentInClip].dispose ();
				playSplat (bootInPoint,poniesInClip[currentInClip],layerGameCtl);
				
				// before proceeding, we must complete two concurrent ops,
				// playing of collapsing of freed space in the ponies clip
				// and flight of the pony (ending with attempt of move)
				var moveVisDone = new SimpleBarrier ();

				// clip collapsing
				new function ()
				{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
					_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));

					// play (asynchronously) collapsing clip animation
					let tweenDone;
					var	done = _Kbt$as.markToDispose((moveVisDone.newOperation ())),
						clipCollapse = _Kbt$as.markToDispose((new Tweener (
						{
							from: 0,
							to: 1,
							over: 250,
							useFunction: TweenFunc.cubic,
							easingMode: "easein",
							onChanged:  kbtBind(this,function(d)
							{
								placeClipSpritesWithDrop (clipPlacedAtX,
									currentInClip,d);
							}
						)})));

					do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[tweenDone] = [_Kbt$msg];_Kbt$consumed = true; if(tweenDone==clipCollapse.eventCompleted ()){;break;}else _Kbt$consumed = false;}} while (false);
					poniesInClip.splice (currentInClip,1);
					clipPonySprites.splice (currentInClip,1);
					clipPonyMods.splice (currentInClip,1);
					if (poniesInClip.length>0)
					{
						// play selection of next pony
						if (currentInClip>=poniesInClip.length)
							currentInClip = poniesInClip.length-1;
						var cxFrom = clipPlacedAtX+ccw*0.5,
							cxTo = Layouts.screenGame.clipCenterX-ccw*currentInClip;
						do {let func=new(moveClipSprites )(cxFrom,cxTo),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
					}
					done ();
				} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();

				// flight of pony launched
				new function ()
				{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
					_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));

					var
						done = _Kbt$as.markToDispose((moveVisDone.newOperation ())),
						cch = Layouts.screenGame.clipCellHeight,
						cx = Layouts.screenGame.clipCenterX,
						cy1 = Layouts.screenGame.clipCenterY,
						flyTo = new Point (bx1+bcw*0.5+bcw*shootAtX,
							by1+bch*0.5+bch*shootAtY),
						flyFrom = new Point (cx,cy1+cch*0.5),
						flyDelta = flyTo.sub (flyFrom);
					
					var flyPony = Kbt.multiApply (_Kbt$as.markToDispose((new Sprite ())),
						{
							setFrame: sprites.coloredPony (ponyColor, 2 ),
							setLayer: layerGameCtl
						}),
						flyMod = flyPony.addMods ();
					
					flyMod.rotation = Math.atan2 (flyDelta.y,flyDelta.x)
						+1.5707963267948966 ;
					var tweenDone,tweenFly = _Kbt$as.markToDispose((new Tweener (
						{
							from: flyFrom,
							to: flyTo,
							over: 300,
							useFunction: TweenFunc.linear,
							onChanged:  kbtBind(this,function(p)
							{
								flyPony.setRefPoint (p);
							}
						)})));
					do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[tweenDone] = [_Kbt$msg];_Kbt$consumed = true; if(tweenDone==tweenFly.eventCompleted ()){;break;}else _Kbt$consumed = false;}} while (false);
					flyPony.dispose ();
					playSplat (flyTo,ponyColor,layerInFrontOfBoard);
					done ();
				} catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}();
				
				// wait for move playment done and visualize changes
				do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[tweenDone] = [_Kbt$msg];_Kbt$consumed = true; if(tweenDone==moveVisDone){;break;}else _Kbt$consumed = false;}} while (false);
				playVisMoveResult (result);
				gameOverCheckTimeout = 2;

				// TODO: if no more ponies in clip, postStaticPersistent outOfAmmo
				if (poniesInClip.length<=0)
				{
					outOfAmmo.withdraw ();
					appEQ.postStaticPersistent (outOfAmmo);
				}
			}
		catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}
	}catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}));

	function rechargeClip ()
	{
		if (ammoLeft>0)
		{
			var poniesToLoad = ammoLeft>8? 8 : ammoLeft;
			ammoLeft -= poniesToLoad;
			updateAmmoLeft ();
			var newPonies = [];
			for (var i=0; i<poniesToLoad; i++)
				newPonies.push (Math.floor (Math.random ()* 7 ));
			poniesClipControl ({ eventType: "clipRecharge", ponies: newPonies });
		}
	}

	// main coroutine
	var prevScreenToRender,prevScreenPhase;
	var base = this.mxOverride (MxScreen,
	{
		draw: function (gc)
		{
			// it is quite like default, but just for reminder how this
			// proc should look in simplest case
			gc.clearRect (0,0, 480 , 640 );
			renderScreen (gc);
			myGuiHost.draw (gc);
			base.draw (gc);

			if (prevScreenToRender)
			{let _Kbt$as=new _Kbt$AutoScope();try
			{
				_Kbt$as.markToDispose((saveGCState (gc)));

				// remember, transformations are applied in reverse way!
				gc.translate ( 480 *0.5, 640 *0.5);
				if (prevScreenPhase>0) // FF bug - scale (0,0) screws the canvas
					gc.scale (prevScreenPhase,prevScreenPhase);
				gc.translate (- 480 *0.5,- 640 *0.5);

				gc.globalAlpha = prevScreenPhase;
				if (prevScreenPhase>0) prevScreenToRender.draw (gc);
			}
		catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}},
		// the routine
		$main: function ()
		{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
			try {
			for (;;)
			{let _Kbt$as=new _Kbt$AutoScope();try
			{
				do {let func=new(me.$waitForActivation )() ,prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl; var prevScreen =func.result;} while (false);

				var gameSuspend = false;
				var frameTick;
				 do { frameTick = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(frameTick)(frameTick===appFrameTimer.tick ())
				),false,kbtBind (this,function(frameTick){
					var tickMs = appLastFrameElapsed;
					for (var sprite in layerBehindBoard.elements ())
						spritesprite.processFrameTick (tickMs);
					for (var sprite in layerOnBoard.elements ())
						sprite.processFrameTick (tickMs);
					for (var sprite in layerOnBoardFX.elements ())
						sprite.processFrameTick (tickMs);
					for (var sprite in layerInFrontOfBoard.elements ())
						sprite.processFrameTick (tickMs);
					for (var sprite in layerFX.elements ())
						sprite.processFrameTick (tickMs);

					if (!gameSuspend && !moveInProgress)
					{
						var newFalloutViss = [];
						var newViss = game.performIdle ();
						if (newViss.length>0) gameOverCheckTimeout = 2;
						else if (gameOverCheckTimeout>0)
							gameOverCheckTimeout--;
						for each (newVis in newViss)
						switch (newVis.type)
						{
						case "visMarkMatch":
							playVisMarkMatch (newVis);
							boardStable = false;
							break;
						case "visRemove":
							playVisRemove (newVis);
							boardStable = false;
							break;
						case "visRemoveExplode":
							playVisRemoveExplode (newVis);
							boardStable = false;
							break;
						case "visRemoveShock":
							playVisRemoveShock (newVis);
							boardStable = false;
							break;
						case "visRemoveChroma":
							playVisRemoveExplode (newVis);
							boardStable = false;
							break;
						case "visCreatePower":
							playVisCreatePower (newVis);
							boardStable = false;
							break;
						case "visCreateShock":
							playVisCreateShock (newVis);
							boardStable = false;
							break;
						case "visCreateChroma":
							playVisCreateChroma (newVis);
							boardStable = false;
							break;
						case "visFallout":
							newFalloutViss.push (newVis);
							boardStable = false;
							break;
						// "visResetCascade": {} // the only reply element if present, also signal for state to go stable
						case "visResetCascade":
							newVis.done = true;
							// reset cascade count and post "check for game over"
							ratioBase = 1;
							ratioPlus = ratioPlusBase+Math.floor (score/100);
							updateRecycleRate ();
							boardStable = true;
							break;
						// "visNewCascade": { cascadeCount: int, poniesRemoved: int }
						case "visNewCascade":
							newVis.done = true;
							// bump cascade count, add to score and to ponies ammo
							score += newVis.poniesRemoved;
							updateScore ();
							ratioBase = newVis.cascadeCount;
							updateRecycleRate ();
							recycleProgress +=
								newVis.poniesRemoved*ratioBase/(ratioBase+ratioPlus);
							if (Math.floor (recycleProgress)>0)
							{
								ammoLeft += Math.floor (recycleProgress);
								recycleProgress -= Math.floor (recycleProgress);
								updateAmmoLeft ();
							}
							boardStable = false;
							break;
						}

						if (newFalloutViss.length>0)
							playVisFallouts (newFalloutViss);

						if (boardStable)
						{
							if (poniesOnTheFly<=0 &&
								gameOverCheckTimeout<=0 &&
								poniesInClip.length<=0 &&
								!evtCheckForGameOver.postedIn ())
								appEQ.postStaticPersistent
									(evtCheckForGameOver);
						}
						else
							evtCheckForGameOver.withdraw ();
					}
				}

				// transition-in animation
				));_Kbt$as.markToDispose(frameTick);} while (false){let _Kbt$as=new _Kbt$AutoScope();try
				{
					prevScreenToRender = prevScreen;
					// cache fading-out screen for performance
					prevScreen.guiHost ().enableBitmapCache (true);
					let transIn = _Kbt$as.markToDispose((new Tweener (
					{
						from: 1,
						to: 0,
						over:  300 ,
						useFunction: TweenFunc.back (0.25),
						onChanged:  kbtBind(this,function(d) { prevScreenPhase = d; }
					)})));

					let transInDone;
					do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[transInDone] = [_Kbt$msg];_Kbt$consumed = true; if(transInDone==transIn.eventCompleted ()){;break;}else _Kbt$consumed = false;}} while (false);
					prevScreenToRender = null;
				}
				catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}me.$confirmActivationCompleted ();
				myGuiHost.enableBitmapCache (false);

				// load first clip
				rechargeClip ();

				/*
				new #[function] ()
				{
					#~(appEQ.subscribe (#[this]));
					for (;;)
					#auto
					{
						throwPony (new Point (240,320),
							new Point (Math.random ()*960-480,-Math.random ()*480),
							Math.floor (Math.random ()*#@PONY_COLORS_TOTAL));
						#[appEQ.sleep] (500);
					}
				} ();
				*/

				for (;;)
				{let _Kbt$as=new _Kbt$AutoScope();try
				{
					let msgTryShoot,
						msgOutOfAmmo,
						msgPowerPonyTip,
						msgShockPonyTip,
						msgChromaFaceTip,
						msgCheckForGameOver;
					do {
						for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[msgTryShoot,msgOutOfAmmo,msgPowerPonyTip,msgShockPonyTip,msgChromaFaceTip,msgCheckForGameOver] = [_Kbt$msg,_Kbt$msg,_Kbt$msg,_Kbt$msg,_Kbt$msg,_Kbt$msg];_Kbt$consumed = true; if(msgTryShoot.eventType=="tryShoot") {[msgOutOfAmmo,msgPowerPonyTip,msgShockPonyTip,msgChromaFaceTip,msgCheckForGameOver] = [undefined,undefined,undefined,undefined,undefined];break;}else 
						 if(msgOutOfAmmo==outOfAmmo) {[msgTryShoot,msgPowerPonyTip,msgShockPonyTip,msgChromaFaceTip,msgCheckForGameOver] = [undefined,undefined,undefined,undefined,undefined];break;}else 
						 if(msgPowerPonyTip==evtPowerPonyTip) {[msgTryShoot,msgOutOfAmmo,msgShockPonyTip,msgChromaFaceTip,msgCheckForGameOver] = [undefined,undefined,undefined,undefined,undefined];break;}else 
						 if(msgShockPonyTip==evtShockPonyTip) {[msgTryShoot,msgOutOfAmmo,msgPowerPonyTip,msgChromaFaceTip,msgCheckForGameOver] = [undefined,undefined,undefined,undefined,undefined];break;}else 
						 if(msgChromaFaceTip==evtChromaFaceTip) {[msgTryShoot,msgOutOfAmmo,msgPowerPonyTip,msgShockPonyTip,msgCheckForGameOver] = [undefined,undefined,undefined,undefined,undefined];break;}else 
						
							if(msgCheckForGameOver==evtCheckForGameOver){[msgTryShoot,msgOutOfAmmo,msgPowerPonyTip,msgShockPonyTip,msgChromaFaceTip] = [undefined,undefined,undefined,undefined,undefined];break;}else _Kbt$consumed = false;}} while (false);
					if (msgTryShoot)
					{let _Kbt$as=new _Kbt$AutoScope();try
					{
						// TODO: check if user can shoot at curBoardX,curBoardY
						// continue if not
						appEQ.post ({ eventType: "shoot" });
					}
					catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}else if (msgOutOfAmmo)
					{
						if (ammoLeft>0) rechargeClip ();
					}
					else if (msgPowerPonyTip && !tipPowerPonyShown)
					{let _Kbt$as=new _Kbt$AutoScope();try
					{
						gameSuspend = true;
						tipPowerPonyShown = true;
						do {let func=new(_Kbt$as.markToDispose((new HvhPopupPowerPony ())).show )("ok"),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
						gameSuspend = false;
					}
					catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}else if (msgShockPonyTip && !tipShockPonyShown)
					{let _Kbt$as=new _Kbt$AutoScope();try
					{
						gameSuspend = true;
						tipShockPonyShown = true;
						do {let func=new(_Kbt$as.markToDispose((new HvhPopupShockPony ())).show )("ok"),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
						gameSuspend = false;
					}
					catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}else if (msgChromaFaceTip && !tipChromaFaceShown)
					{let _Kbt$as=new _Kbt$AutoScope();try
					{
						gameSuspend = true;
						tipChromaFaceShown = true;
						do {let func=new(_Kbt$as.markToDispose((new HvhPopupChromaFace ())).show )("ok"),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
						gameSuspend = false;
					}
					catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}else if (msgCheckForGameOver)
					{let _Kbt$as=new _Kbt$AutoScope();try
					{
						if (ammoLeft<=0 && poniesInClip.length<=0)
						{
							// out of ammo at checkpoint - game over
							do {let func=new(_Kbt$as.markToDispose((new HvhPopupQuery
								("GAME OVER","You are out of pony scrap.\nYour final score is "+score))
								).show )("ok"),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
							me.response = "gameOver";
							do {let func=new(me.$waitForActivation )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false); // return response to dispatcher and sleep
							me.$confirmActivationCompleted (); // dispatcher invoked us again
							// (actually, it won't, but just for protocol)
							me.response = null;
						}
						else if (ammoLeft>0 && !outOfAmmo.postedIn ())
							appEQ.postStaticPersistent (outOfAmmo);
					}
				catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}
			catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}
			catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}
			catch (e)
			{
				appLog.writef ("<hr>Fuckup detected<hr>\n");
				if (e.elements)
				for (var x in e.elements ()) appLog.writef (x+"<br>\n");
				else appLog.writef (e.message);
			}
		}
	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}});

	this.mxFreeze ();
	this.$start ();
}
catch(e){this.markToDispose(function(){throw e;});this.dispose();}}


//
// main.kbtjsx: screenman.kbtjsx, scr_mainmenu.kbtjsx, ...
// Application main entry point
//

var mainInstance; // will be set to main

// helper, coroutine blocker: synchronously activate the screen, return when
// screen is activated
function activateScreen (screen)
{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
	screen.beginActivate ();
	let screenActivated;
	do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[screenActivated] = [_Kbt$msg];_Kbt$consumed = true;
		if(screenActivated==screen.eventActivated ()){;break;}else _Kbt$consumed = false;}} while (false);
}

// helper, coroutine blocker: block until the screen is deactivated
catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}function waitForScreenDeactivated (screen)
{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
	let screenDeactivated;
	do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[screenDeactivated] = [_Kbt$msg];_Kbt$consumed = true;
		if(screenDeactivated==screen.eventDeactivated ()){;break;}else _Kbt$consumed = false;}} while (false);
}

catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}function main ()
{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
	_Kbt$as.markToDispose(kbtBind(this,function(){
		// on exit from the block (in this location, from the whole program
		// as well)
		appLog.writef ("<b>Program exited</b><br>\n");
		delete mainInstance;
		//delete appEQ;
	}

	));_Kbt$as.markToDispose((appEQ.subscribe (_Kbt$func)));
	do {let func=new(appEQ.checkpoint )(),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false); // to ensure we entered event process iteration

	// install low level mouse handlers
	document.onmousedown = function (e)
	{
		if (appEQ)
		{
			e.eventType = "mouseDown";
			e.X = e.pageX-appCanvas.offsetLeft;
			e.Y = e.pageY-appCanvas.offsetTop;
			appEQ.send (e);
		}
	};
	document.onmouseup = function (e)
	{
		if (appEQ)
		{
			e.eventType = "mouseUp";
			e.X = e.pageX-appCanvas.offsetLeft;
			e.Y = e.pageY-appCanvas.offsetTop;
			appEQ.send (e);
		}
	};
	document.onmousemove = function (e)
	{
		if (appEQ)
		{
			e.eventType = "mouseMove";
			e.X = e.pageX-appCanvas.offsetLeft;
			e.Y = e.pageY-appCanvas.offsetTop;
			appEQ.send (e);
		}
	};

	// install frame ticker
	appFrameTimer = new Timer ();
	appFrameTimer.start (1000/ 30 );
	appFrameTimer.resetTiming ();
	_Kbt$as.markToDispose(kbtBind(this,function(){ do  { if (appFrameTimer) { (appFrameTimer).dispose (); appFrameTimer= null; } } while (0) ; }

	));{let _Kbt$as=new _Kbt$AutoScope();try
	{
		// setup UI responsiveness framework:
		// mouseMove/mouseDown/mouseUp are offered to current screen's gui host
		// if not accepted (or no screen is current), re-posted as eventType
		// rawTouchMove/rawTouchDown/rawTouchUp.
		function tryDispatchTouchMessage (msg)
		{
			var currentScreen = ScreenManager.currentScreen (),
				currentPopup = PopupManager.currentPopup ();
			if (currentPopup!=null)
			{
				if (PopupManager.activationPhase>=1)
					return currentPopup.guiHost ().dispatchMouseMessage (msg);
				else
					return false;
			}
			else return currentScreen &&
				currentScreen.guiHost ().dispatchMouseMessage (msg);
		}
		let eMouseMove,eMouseDown,eMouseUp;
		
			do { eMouseMove = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(eMouseMove)(eMouseMove.eventType=="mouseMove")
			),false,kbtBind (this,function(eMouseMove){
				var currentScreen = ScreenManager.currentScreen ();
				if (!tryDispatchTouchMessage (eMouseMove))
					appEQ.post ({
						eventType: "rawTouchMove",
						X: eMouseMove.X,
						Y: eMouseMove.Y
					});
			} ));_Kbt$as.markToDispose(eMouseMove);
			 eMouseDown = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(eMouseDown)(eMouseDown.eventType=="mouseDown")
			),false,kbtBind (this,function(eMouseDown){
				var currentScreen = ScreenManager.currentScreen ();
				if (!tryDispatchTouchMessage (eMouseDown))
					appEQ.post ({
						eventType: "rawTouchDown",
						X: eMouseDown.X,
						Y: eMouseDown.Y
					});
			} ));_Kbt$as.markToDispose(eMouseDown);
			 eMouseUp = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(eMouseUp)(eMouseUp.eventType=="mouseUp")
			),false,kbtBind (this,function(eMouseUp){
				var currentScreen = ScreenManager.currentScreen ();
				if (!tryDispatchTouchMessage (eMouseUp))
					appEQ.post ({
						eventType: "rawTouchUp",
						X: eMouseUp.X,
						Y: eMouseUp.Y
					});
			}));_Kbt$as.markToDispose(eMouseUp);} while (false);

		// setup per-frame rendering - draw the current screen each frame
		let frameUpdate;
		 do { frameUpdate = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(frameUpdate)(frameUpdate===appFrameTimer.tick ())
		),false,kbtBind (this,function(frameUpdate){
			appLastFrameElapsed = appFrameTimer.getTiming ();
			appFrameTimer.resetTiming ();
			appGC.clearRect (0,0, 480 , 640 );
			let currentScreen = ScreenManager.currentScreen (),
				currentPopup = PopupManager.currentPopup ();
			if (currentScreen)
				currentScreen.draw (appGC);
			if (currentPopup)
			{let _Kbt$as=new _Kbt$AutoScope();try
			{
				_Kbt$as.markToDispose((saveGCState (appGC)));
				var drawVeil = false;
				if (PopupManager.totalPopups ()>1 && (drawVeil=true))
					appGC.globalAlpha =  0.5 ;
				else if (PopupManager.totalPopups ()==1 && (drawVeil=true))
					appGC.globalAlpha =  0.5 *PopupManager.activationPhase;
				else
					appGC.globalAlpha =  0.5 ;
				if (drawVeil)
				{
					appGC.fillStyle =  "#000000" ;
					appGC.fillRect (0,0, 480 , 640 );
				}
				appGC.globalAlpha = appGC.globalAlpha/ 0.5 ;

				// remember, transformations are applied in reverse way!
				var scale = TweenFunc.back(0.25)
					(PopupManager.activationPhase);
				appGC.translate ( 480 *0.5, 640 *0.5);
				if (scale>0) // FF bug - scale (0,0) screws the canvas
					appGC.scale (scale,scale);
				appGC.translate (- 480 *0.5,- 640 *0.5);
				if (scale>0) currentPopup.draw (appGC);
			}
		catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}));_Kbt$as.markToDispose(frameUpdate);} while (false);

		// pre-prepare loading screen
		let screenLoading = new   function ()
		{KbtMixin.enable(this);this.mxEnable(MxDisposable);try{
			// loading screen class
			this.mxEnable (MxScreen);
			var me = this,myGuiHost = this.guiHost ();

			var
				background = Kbt.multiApply (
				this.markToDispose((new WtSolidColor ())),
				{
					setParent: myGuiHost,
					setRect: new Rect (0,0, 480 , 640 ),
					setColor: "#FFFFFF"
				}),
				progressBar = Kbt.multiApply (
				this.markToDispose((new MxWtProgressBar ())),
				{
					setParent: myGuiHost,
					setRect: Layouts.screenLoading.progressBar,
					setCurrent: 0,
					setMax: 100
				}),
				progressLabel = Kbt.multiApply (
				this.markToDispose((new WtLabel ())),
				{
					setParent: myGuiHost,
					setRect: Layouts.screenLoading.progressLabel,
					setColor: "#000000",
					setAligns: { h: "center", v: "top" },
					setAutoLines: 1,
					setFontDesc: { height: 15, bold: true, italic: false }, 
					setText: "0%",
					enableClipping: false
				}),
				buttGo = null;

			// main things
			var i = 0;
			var base = this.mxOverride (MxScreen,
			{
				draw: function (gc)
				{
					// it is quite like default, but just for reminder how this
					// proc should look in simplest case
					myGuiHost.draw (gc);
				},
				$main: function ()
				{var core,_Kbt$msg,_Kbt$reply,_Kbt$consumed=false,_Kbt$done=false,_Kbt$result,_Kbt$isRunning=false,_Kbt$unhandledExc=false,_Kbt$func=function(msg){try{kbtAssert(!_Kbt$isRunning,_Kbt$crtMsgReenter);_Kbt$isRunning=true;_Kbt$msg=msg;core.next();return copyResult();}catch(e){if(!_Kbt$unhandledExc)return copyResult();throw e;}finally{_Kbt$isRunning=false;}};function Core (){if(false) yield undefined;try{var _Kbt$observers=new KbtList();function _Kbt$doObservers(){for(let observer in _Kbt$observers.elements()){if (observer._Kbt$suspendLevel<=0&&observer._Kbt$condition(_Kbt$msg)){if(observer._Kbt$isInterruptor){intrTriggered=true;let func=new observer._Kbt$code();_Kbt$consumed=true;try{observer._Kbt$suspendLevel++;for(let x in func){yield true;_Kbt$reply=undefined;}}finally{observer._Kbt$suspendLevel--;}break;}else observer._Kbt$code (_Kbt$msg);}}}{let _Kbt$as=new _Kbt$AutoScope();try{
					do {let func=new(me.$waitForActivation )() ,prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl; var prevScreen =func.result;} while (false);
					// prevScreen is always null here, as screenLoading is
					// always the first screen, and is not actually needed,
					// but just as a reminder

					me.$confirmActivationCompleted ();

					var blockme;
					do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[blockme] = [_Kbt$msg];_Kbt$consumed = true; if(false){;break;}else _Kbt$consumed = false;}} while (false);
				}
			catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}});

			this.koolProperty = 0;
			this.updateProgress = function (current,max)
			{
				Kbt.multiApply (progressBar,
				{
					setCurrent: current,
					setMax: max
				});
				progressLabel.setText (Kbt.format ("%d%%%",
				{ "d": Math.round (current*100/max) }));
			};

			this.showGo = function ()
			{
				if (!buttGo)
				{
					var size = imageClips.BUTT_GO.size;
					buttGo = Kbt.multiApply (me.markToDispose((new WtImageClipButton ())),
					{
						setParent: myGuiHost,
						setRect: new Rect (( 480 -size.width)*0.5,
							Layouts.screenLoading.goButtY,
							size.width,size.height),
						setPressedPicture: imageClips.BUTT_GO (0,0),
						setReleasedPicture: imageClips.BUTT_GO (0,1)
					});
				}
			};

			this.goClick = function ()
			{
				return buttGo? buttGo.click () : null;
			};

			this.mxFreeze ();
			this.$start ();
		}catch(e){this.markToDispose(function(){throw e;});this.dispose();}};
		// cleanup fallback
		_Kbt$as.markToDispose(kbtBind(this,function(){ do  { if (screenLoading) { (screenLoading).dispose (); screenLoading= null; } } while (0) ; }
		
		// load images et al
		));{let _Kbt$as=new _Kbt$AutoScope();try
		{
			let allLoadedBarrier = _Kbt$as.markToDispose((new MxAsyncOpBarrier ().retain ()));
			let loadTick;
			
				do { loadTick = new _Kbt$Observer (_Kbt$observers,kbtBind(this,function(loadTick)(loadTick==allLoadedBarrier.updateSignal ())
				),false,kbtBind (this,function(loadTick){
					// update progress bar on loading screen
					screenLoading.updateProgress (
						allLoadedBarrier.itemsCompleted (),
						allLoadedBarrier.itemsTotal ());
				}));_Kbt$as.markToDispose(loadTick);} while (false);

			// activate loading screen
			do {let func=new(activateScreen )(screenLoading),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);

			// launch loading operations
			beginLoadImages (allLoadedBarrier);

			// expect for them to complete
			let allLoaded = _Kbt$as.markToDispose((makeAsyncSignal ({ eventType: "asyncSignal" })));
			allLoaded.asgWaitFor (allLoadedBarrier);

			let eAllLoaded;
			do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[eAllLoaded] = [_Kbt$msg];_Kbt$consumed = true; if(eAllLoaded==allLoaded){;break;}else _Kbt$consumed = false;}} while (false);
		}
		// all images (and whatever else) loaded

		// initialize subresources
		catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}initImageClips ();
		initSpritesAndAnimations ();
		screenLoading.showGo (); // show "GO" button on loading screen

		// wait until "GO" is clicked
		let goClick;
		do { for(;;){yield true;if(_Kbt$msg===undefined){_Kbt$consumed=false;continue;}_Kbt$reply=undefined;let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(intrTriggered)continue;[goClick] = [_Kbt$msg];_Kbt$consumed = true; if(goClick==screenLoading.goClick ()){;break;}else _Kbt$consumed = false;}} while (false);

		// other screens
		var
			screenMainMenu = null,
			screenGame = null;

		// disposed screen revivers
		function reviveScreenMainMenu ()
		{
			if (!screenMainMenu) screenMainMenu = new ScreenMainMenu ();
			return screenMainMenu;
		}
		function reviveScreenGame ()
		{
			if (!screenGame) screenGame = new ScreenGame ();
			return screenGame;
		}

		_Kbt$as.markToDispose(kbtBind(this,function(){
			do  { if (screenMainMenu) { (screenMainMenu).dispose (); screenMainMenu= null; } } while (0) ;
			do  { if (screenGame) { (screenGame).dispose (); screenGame= null; } } while (0) ;
		}

		// begin main loop
		));
		var gameOver = false;
		for (;;)
		{let _Kbt$as=new _Kbt$AutoScope();try
		{
			// show main menu
			do {let func=new(activateScreen )(reviveScreenMainMenu ()),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			if (gameOver)
			{
				gameOver = false;
				do  { if (screenGame) { (screenGame).dispose (); screenGame= null; } } while (0) ;
			}
			do  { if (screenLoading) { (screenLoading).dispose (); screenLoading= null; } } while (0) ;

			// wait for user choice
			do {let func=new(waitForScreenDeactivated )(screenMainMenu),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
			if (screenMainMenu.response=="newGame")
			{
				// new game: show game
				do {let func=new(activateScreen )(reviveScreenGame ()),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
				do  { if (screenMainMenu) { (screenMainMenu).dispose (); screenMainMenu= null; } } while (0) ;

				// wait for game over/exit to menu
				do {let func=new(waitForScreenDeactivated )(screenGame),prevRepl=_Kbt$reply;try{while(!func.done){yield true;for(;;){let intrTriggered=false,observerChecker=_Kbt$doObservers();try{for(let observer in observerChecker){intrTriggered=true;yield true;_Kbt$reply=undefined;}}finally{observerChecker.close();}if(!intrTriggered)break;yield true;}func(_Kbt$msg);_Kbt$reply=func.reply;_Kbt$consumed=func.msgConsumed;}}finally {func.dispose()};_Kbt$reply = prevRepl;} while (false);
				if (screenGame.response=="gameOver") gameOver = true;
			}
			else if (screenMainMenu.response=="endGame")
			{
				do  { if (screenGame) { (screenGame).dispose (); screenGame= null; } } while (0) ;
			}
		}
	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}
	catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}appLog.writef ("<b>Program finished</b><br>\n");
}
catch(e){_Kbt$as.exceptions.push(e);}finally{try{_Kbt$as.dispose();}catch(e){_Kbt$as.exceptions.push(e);}finally{if(_Kbt$as.exceptions.count()>0)throw _Kbt$as.exceptions;}}}}catch(e){_Kbt$unhandledExc=true;throw e;}finally{_Kbt$done=true;}}var core=new Core(),result;function copyResult(){_Kbt$func.done=_Kbt$done;_Kbt$func.result=_Kbt$result;_Kbt$func.reply=_Kbt$reply;_Kbt$func.msgConsumed=_Kbt$consumed;return !_Kbt$done;}KbtMixin.enable(_Kbt$func);_Kbt$func.mxEnable(MxDisposable);_Kbt$func.markToDispose(function(){kbtAssert (!_Kbt$isRunning,_Kbt$crtMsgDisposeUnsafe);core.close();});_Kbt$func();return _Kbt$func;}

//
// end of precompiled code
//

//var cp2 = new Date ().getTime ();
//appLog.writef ("Loaded in "+(cp2-cp1)+" ms<br>\n");

try {
	// run our everything
	mainInstance = new main ();
	appEQ.processEvents ();
	// ...and now it goes on its own
}
catch (e)
{
	appLog.writef ("<hr>Fuckup detected<hr>\n");
	if (e.elements)
	for (var x in e.elements ()) appLog.writef (x+"<br>\n");
	else appLog.writef (e.message);
}
</script>

<script>
// check the outcome of our version check
if (typeof (appGC)=="undefined")
{
	document.write ("<img src=img/alas.jpg><br>\n"+
	"This application requires browser that supports JS 1.7+ and HTML 5.<br>\n"
	+"The only browser currently capable of these both is <a href=http://www.mozilla.org/ru/firefox>Firefox</a>.");
}
</script>
</body>
</html>